mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
add language: java
This commit is contained in:
parent
c582bc8757
commit
26d8727db7
@ -17,6 +17,8 @@ tempfile = "3"
|
|||||||
libc = "0.2.76"
|
libc = "0.2.76"
|
||||||
log = "0.4.0"
|
log = "0.4.0"
|
||||||
env_logger = "0.8.1"
|
env_logger = "0.8.1"
|
||||||
|
nix = "0.19.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build ={ version = "0.3" }
|
tonic-build ={ version = "0.3" }
|
||||||
|
cc = "1.0"
|
||||||
1
build.rs
1
build.rs
@ -1,4 +1,5 @@
|
|||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tonic_build::compile_protos("proto/river.proto")?;
|
tonic_build::compile_protos("proto/river.proto")?;
|
||||||
|
cc::Build::new().file("src/memory.c").compile("memory");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
5
example/java/1000/Main.java
Normal file
5
example/java/1000/Main.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
0
example/java/1000/in.txt
Normal file
0
example/java/1000/in.txt
Normal file
1
example/java/1000/out.txt
Normal file
1
example/java/1000/out.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello World!
|
||||||
12
example/java/1001/Main.java
Normal file
12
example/java/1001/Main.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Scanner reader = new Scanner(System.in);
|
||||||
|
int a, b;
|
||||||
|
a = reader.nextInt();
|
||||||
|
b = reader.nextInt();
|
||||||
|
System.out.println(a + b);
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
example/java/1001/in.txt
Normal file
1
example/java/1001/in.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2 3
|
||||||
1
example/java/1001/out.txt
Normal file
1
example/java/1001/out.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
5
|
||||||
@ -13,6 +13,8 @@ def judge(path, language):
|
|||||||
filename = "main.cpp"
|
filename = "main.cpp"
|
||||||
elif language == river_pb2.Python:
|
elif language == river_pb2.Python:
|
||||||
filename = "main.py"
|
filename = "main.py"
|
||||||
|
elif language == river_pb2.Java:
|
||||||
|
filename = "Main.java"
|
||||||
with open(path.joinpath(filename), "rb") as fr:
|
with open(path.joinpath(filename), "rb") as fr:
|
||||||
code = fr.read()
|
code = fr.read()
|
||||||
with open(path.joinpath("in.txt"), "rb") as fr:
|
with open(path.joinpath("in.txt"), "rb") as fr:
|
||||||
@ -25,19 +27,30 @@ def judge(path, language):
|
|||||||
judge_type=river_pb2.Standard,
|
judge_type=river_pb2.Standard,
|
||||||
compile_data=river_pb2.CompileData(code=code),
|
compile_data=river_pb2.CompileData(code=code),
|
||||||
)
|
)
|
||||||
|
# if language == river_pb2.Java:
|
||||||
|
# import time
|
||||||
|
# time.sleep(1000)
|
||||||
# judge
|
# judge
|
||||||
yield river_pb2.JudgeRequest(
|
yield river_pb2.JudgeRequest(
|
||||||
language=language,
|
language=language,
|
||||||
judge_type=river_pb2.Standard,
|
judge_type=river_pb2.Standard,
|
||||||
judge_data=river_pb2.JudgeData(
|
judge_data=river_pb2.JudgeData(
|
||||||
in_data=in_data, out_data=out_data, time_limit=1000, memory_limit=65535
|
in_data=in_data, out_data=out_data, time_limit=10000, memory_limit=65535
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
# if language == river_pb2.Java:
|
||||||
|
# import time
|
||||||
|
# time.sleep(1000)
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
with grpc.insecure_channel("localhost:4003") as channel:
|
with grpc.insecure_channel("localhost:4003") as channel:
|
||||||
stub = river_pb2_grpc.RiverStub(channel)
|
stub = river_pb2_grpc.RiverStub(channel)
|
||||||
|
for path in Path("java").iterdir():
|
||||||
|
print(f"开始评测 {path}")
|
||||||
|
for item in stub.Judge(judge(path, river_pb2.Java)):
|
||||||
|
print(item)
|
||||||
|
print(f"{path} 评测完成")
|
||||||
for path in Path("c").iterdir():
|
for path in Path("c").iterdir():
|
||||||
print(f"开始评测 {path}")
|
print(f"开始评测 {path}")
|
||||||
for item in stub.Judge(judge(path, river_pb2.C)):
|
for item in stub.Judge(judge(path, river_pb2.C)):
|
||||||
|
|||||||
16
src/allow.rs
16
src/allow.rs
@ -7,17 +7,22 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
|
|||||||
allow_syscall(libc::SYS_arch_prctl),
|
allow_syscall(libc::SYS_arch_prctl),
|
||||||
// allow_syscall(libc::SYS_brk),
|
// allow_syscall(libc::SYS_brk),
|
||||||
allow_syscall(libc::SYS_chmod),
|
allow_syscall(libc::SYS_chmod),
|
||||||
|
allow_syscall(libc::SYS_clock_adjtime),
|
||||||
|
allow_syscall(libc::SYS_clock_getres),
|
||||||
allow_syscall(libc::SYS_clock_gettime),
|
allow_syscall(libc::SYS_clock_gettime),
|
||||||
allow_syscall(libc::SYS_clone),
|
allow_syscall(libc::SYS_clone),
|
||||||
allow_syscall(libc::SYS_close),
|
allow_syscall(libc::SYS_close),
|
||||||
|
allow_syscall(libc::SYS_connect),
|
||||||
allow_syscall(libc::SYS_dup),
|
allow_syscall(libc::SYS_dup),
|
||||||
allow_syscall(libc::SYS_dup2),
|
allow_syscall(libc::SYS_dup2),
|
||||||
allow_syscall(libc::SYS_execve),
|
allow_syscall(libc::SYS_execve),
|
||||||
allow_syscall(libc::SYS_exit),
|
allow_syscall(libc::SYS_exit),
|
||||||
allow_syscall(libc::SYS_exit_group),
|
allow_syscall(libc::SYS_exit_group),
|
||||||
|
allow_syscall(libc::SYS_fchdir),
|
||||||
allow_syscall(libc::SYS_fcntl),
|
allow_syscall(libc::SYS_fcntl),
|
||||||
allow_syscall(libc::SYS_fork),
|
allow_syscall(libc::SYS_fork),
|
||||||
allow_syscall(libc::SYS_fstat),
|
allow_syscall(libc::SYS_fstat),
|
||||||
|
allow_syscall(libc::SYS_ftruncate),
|
||||||
allow_syscall(libc::SYS_futex),
|
allow_syscall(libc::SYS_futex),
|
||||||
allow_syscall(libc::SYS_getcwd),
|
allow_syscall(libc::SYS_getcwd),
|
||||||
allow_syscall(libc::SYS_getdents),
|
allow_syscall(libc::SYS_getdents),
|
||||||
@ -25,6 +30,8 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
|
|||||||
allow_syscall(libc::SYS_geteuid),
|
allow_syscall(libc::SYS_geteuid),
|
||||||
allow_syscall(libc::SYS_getgid),
|
allow_syscall(libc::SYS_getgid),
|
||||||
allow_syscall(libc::SYS_getpid),
|
allow_syscall(libc::SYS_getpid),
|
||||||
|
allow_syscall(libc::SYS_getsockname),
|
||||||
|
allow_syscall(libc::SYS_getsockopt),
|
||||||
allow_syscall(libc::SYS_gettid),
|
allow_syscall(libc::SYS_gettid),
|
||||||
allow_syscall(libc::SYS_getrandom),
|
allow_syscall(libc::SYS_getrandom),
|
||||||
allow_syscall(libc::SYS_getrlimit),
|
allow_syscall(libc::SYS_getrlimit),
|
||||||
@ -33,12 +40,16 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
|
|||||||
allow_syscall(libc::SYS_ioctl),
|
allow_syscall(libc::SYS_ioctl),
|
||||||
allow_syscall(libc::SYS_lseek),
|
allow_syscall(libc::SYS_lseek),
|
||||||
allow_syscall(libc::SYS_lstat),
|
allow_syscall(libc::SYS_lstat),
|
||||||
|
allow_syscall(libc::SYS_madvise),
|
||||||
// allow_syscall(libc::SYS_mmap),
|
// allow_syscall(libc::SYS_mmap),
|
||||||
allow_syscall(libc::SYS_mkdir),
|
allow_syscall(libc::SYS_mkdir),
|
||||||
allow_syscall(libc::SYS_mprotect),
|
allow_syscall(libc::SYS_mprotect),
|
||||||
// allow_syscall(libc::SYS_munmap),
|
// allow_syscall(libc::SYS_munmap),
|
||||||
|
allow_syscall(libc::SYS_nanosleep),
|
||||||
allow_syscall(libc::SYS_open),
|
allow_syscall(libc::SYS_open),
|
||||||
allow_syscall(libc::SYS_openat),
|
allow_syscall(libc::SYS_openat),
|
||||||
|
allow_syscall(libc::SYS_prctl),
|
||||||
|
allow_syscall(libc::SYS_pread64),
|
||||||
allow_syscall(libc::SYS_prlimit64),
|
allow_syscall(libc::SYS_prlimit64),
|
||||||
allow_syscall(libc::SYS_read),
|
allow_syscall(libc::SYS_read),
|
||||||
allow_syscall(libc::SYS_readlink),
|
allow_syscall(libc::SYS_readlink),
|
||||||
@ -46,10 +57,15 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
|
|||||||
allow_syscall(libc::SYS_rt_sigaction),
|
allow_syscall(libc::SYS_rt_sigaction),
|
||||||
allow_syscall(libc::SYS_rt_sigprocmask),
|
allow_syscall(libc::SYS_rt_sigprocmask),
|
||||||
allow_syscall(libc::SYS_rt_sigreturn),
|
allow_syscall(libc::SYS_rt_sigreturn),
|
||||||
|
allow_syscall(libc::SYS_sched_getaffinity),
|
||||||
|
allow_syscall(libc::SYS_sched_yield),
|
||||||
allow_syscall(libc::SYS_select),
|
allow_syscall(libc::SYS_select),
|
||||||
allow_syscall(libc::SYS_set_robust_list),
|
allow_syscall(libc::SYS_set_robust_list),
|
||||||
allow_syscall(libc::SYS_set_tid_address),
|
allow_syscall(libc::SYS_set_tid_address),
|
||||||
|
allow_syscall(libc::SYS_setsockopt),
|
||||||
allow_syscall(libc::SYS_sigaltstack),
|
allow_syscall(libc::SYS_sigaltstack),
|
||||||
|
allow_syscall(libc::SYS_socket),
|
||||||
|
allow_syscall(libc::SYS_socketpair),
|
||||||
allow_syscall(libc::SYS_stat),
|
allow_syscall(libc::SYS_stat),
|
||||||
allow_syscall(libc::SYS_sysinfo),
|
allow_syscall(libc::SYS_sysinfo),
|
||||||
allow_syscall(libc::SYS_umask),
|
allow_syscall(libc::SYS_umask),
|
||||||
|
|||||||
@ -44,7 +44,7 @@ pub async fn judger(
|
|||||||
Some(Language::Node) => "node main.js",
|
Some(Language::Node) => "node main.js",
|
||||||
Some(Language::TypeScript) => "node main.js",
|
Some(Language::TypeScript) => "node main.js",
|
||||||
Some(Language::Go) => "./a.out",
|
Some(Language::Go) => "./a.out",
|
||||||
Some(Language::Java) => "Main.java",
|
Some(Language::Java) => "/usr/bin/java -cp . Main",
|
||||||
None => return Err(Error::LanguageNotFound(request.language)),
|
None => return Err(Error::LanguageNotFound(request.language)),
|
||||||
};
|
};
|
||||||
let process = Process::new(
|
let process = Process::new(
|
||||||
@ -54,9 +54,15 @@ pub async fn judger(
|
|||||||
data.time_limit,
|
data.time_limit,
|
||||||
data.memory_limit,
|
data.memory_limit,
|
||||||
)?;
|
)?;
|
||||||
|
debug!("run command: {}", cmd);
|
||||||
|
|
||||||
// 开始执行并等待返回结果
|
// 开始执行并等待返回结果
|
||||||
let runner = process.runner.clone();
|
let mut runner = process.runner.clone();
|
||||||
|
// 为 Java 虚拟机取消内存限制和 trace(万恶的 JVM)
|
||||||
|
if request.language == Language::Java as i32 {
|
||||||
|
runner.memory_limit = -1;
|
||||||
|
runner.traceme = false;
|
||||||
|
}
|
||||||
let status = runner.await?;
|
let status = runner.await?;
|
||||||
|
|
||||||
resp.set_process_status(&status);
|
resp.set_process_status(&status);
|
||||||
@ -126,7 +132,7 @@ pub async fn compile(
|
|||||||
Some(Language::Node) => "/usr/bin/node /plugins/js/validate.js main.js",
|
Some(Language::Node) => "/usr/bin/node /plugins/js/validate.js main.js",
|
||||||
Some(Language::TypeScript) => "/usr/bin/tsc main.ts",
|
Some(Language::TypeScript) => "/usr/bin/tsc main.ts",
|
||||||
Some(Language::Go) => "/usr/bin/go build -ldflags \"-s -w\" main.go",
|
Some(Language::Go) => "/usr/bin/go build -ldflags \"-s -w\" main.go",
|
||||||
Some(Language::Java) => "/usr/bin/java -cp . Main",
|
Some(Language::Java) => "/usr/bin/javac Main.java",
|
||||||
None => return Err(Error::LanguageNotFound(request.language)),
|
None => return Err(Error::LanguageNotFound(request.language)),
|
||||||
};
|
};
|
||||||
debug!("build command: {}", cmd);
|
debug!("build command: {}", cmd);
|
||||||
@ -142,6 +148,10 @@ pub async fn compile(
|
|||||||
|
|
||||||
let mut runner = process.runner.clone();
|
let mut runner = process.runner.clone();
|
||||||
runner.traceme = false;
|
runner.traceme = false;
|
||||||
|
// 为 Java 虚拟机取消内存限制(万恶的 JVM)
|
||||||
|
if request.language == Language::Java as i32 {
|
||||||
|
runner.memory_limit = -1;
|
||||||
|
}
|
||||||
let status = runner.await?;
|
let status = runner.await?;
|
||||||
resp.set_process_status(&status);
|
resp.set_process_status(&status);
|
||||||
if status.exit_code != 0 || status.signal != 0 {
|
if status.exit_code != 0 || status.signal != 0 {
|
||||||
|
|||||||
160
src/memory.c
Normal file
160
src/memory.c
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* in: ' 42964 kB'
|
||||||
|
* out: 42964
|
||||||
|
* */
|
||||||
|
int GetNumByVmLine(char body[])
|
||||||
|
{
|
||||||
|
int offset, ans, start;
|
||||||
|
offset = ans = 0;
|
||||||
|
start = 0; // FALSE on C
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (!start)
|
||||||
|
{
|
||||||
|
if (isdigit(body[offset]))
|
||||||
|
{
|
||||||
|
start = 1; // TRUE on C
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start)
|
||||||
|
{
|
||||||
|
if (isdigit(body[offset]))
|
||||||
|
{
|
||||||
|
ans *= 10;
|
||||||
|
ans += (int)(body[offset] - '0');
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
long MemoryUsage(int fd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
ssize_t len;
|
||||||
|
char body[4096];
|
||||||
|
long vm_data = 0, vm_stk = 0;
|
||||||
|
|
||||||
|
if ((len = pread(fd, body, 4096, 0)) == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
switch (body[i])
|
||||||
|
{
|
||||||
|
case 'V':
|
||||||
|
goto V;
|
||||||
|
default:
|
||||||
|
goto NEXTLINE;
|
||||||
|
}
|
||||||
|
V:
|
||||||
|
i++;
|
||||||
|
switch (body[i])
|
||||||
|
{
|
||||||
|
case 'm':
|
||||||
|
goto Vm;
|
||||||
|
default:
|
||||||
|
goto NEXTLINE;
|
||||||
|
}
|
||||||
|
Vm:
|
||||||
|
i++;
|
||||||
|
switch (body[i])
|
||||||
|
{
|
||||||
|
case 'R':
|
||||||
|
i += 2;
|
||||||
|
goto VmRSS;
|
||||||
|
case 'D':
|
||||||
|
i += 3;
|
||||||
|
goto VmData;
|
||||||
|
case 'S':
|
||||||
|
goto VmS;
|
||||||
|
case 'E':
|
||||||
|
i += 2;
|
||||||
|
goto VmExe;
|
||||||
|
case 'L':
|
||||||
|
goto VmL;
|
||||||
|
default:
|
||||||
|
goto NEXTLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmRSS:
|
||||||
|
i += 2;
|
||||||
|
// vm_rss = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
VmData:
|
||||||
|
i += 2;
|
||||||
|
vm_data = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
VmS:
|
||||||
|
i++;
|
||||||
|
switch (body[i])
|
||||||
|
{
|
||||||
|
case 't':
|
||||||
|
i++;
|
||||||
|
goto VmStk;
|
||||||
|
case 'i':
|
||||||
|
i += 2;
|
||||||
|
goto VmSize;
|
||||||
|
default:
|
||||||
|
goto NEXTLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmStk:
|
||||||
|
i += 2;
|
||||||
|
vm_stk = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
VmSize:
|
||||||
|
i += 2;
|
||||||
|
// vm_size = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
VmExe:
|
||||||
|
i += 2;
|
||||||
|
// vm_exe = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
VmL:
|
||||||
|
i++;
|
||||||
|
switch (body[i])
|
||||||
|
{
|
||||||
|
case 'i':
|
||||||
|
i++;
|
||||||
|
goto VmLib;
|
||||||
|
}
|
||||||
|
|
||||||
|
VmLib:
|
||||||
|
i += 2;
|
||||||
|
// vm_lib = GetNumByVmLine(body + i);
|
||||||
|
goto NEXTLINE;
|
||||||
|
|
||||||
|
NEXTLINE:
|
||||||
|
while (body[i] != '\n')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm_data + vm_stk;
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ pub fn standard_result(out: &[u8], ans: &[u8]) -> Result<JudgeResult> {
|
|||||||
let mut ans_offset = 0;
|
let mut ans_offset = 0;
|
||||||
// 没有 PE,PE 直接 WA
|
// 没有 PE,PE 直接 WA
|
||||||
let mut r = JudgeResult::Accepted;
|
let mut r = JudgeResult::Accepted;
|
||||||
while out_offset < out_len && ans_offset < ans_len {
|
while out_offset <= out_len && ans_offset <= ans_len {
|
||||||
let (out_start, out_end, out_exists) = next_line(&out, out_offset, out_len);
|
let (out_start, out_end, out_exists) = next_line(&out, out_offset, out_len);
|
||||||
let (ans_start, ans_end, ans_exists) = next_line(&ans, ans_offset, ans_len);
|
let (ans_start, ans_end, ans_exists) = next_line(&ans, ans_offset, ans_len);
|
||||||
if !out_exists || !ans_exists {
|
if !out_exists || !ans_exists {
|
||||||
@ -159,4 +159,25 @@ mod tests {
|
|||||||
let out: &[u8] = "Hello World!\t\t\t\t\n\n\n\n \n\n\n\n\t\t\t\t".as_bytes();
|
let out: &[u8] = "Hello World!\t\t\t\t\n\n\n\n \n\n\n\n\t\t\t\t".as_bytes();
|
||||||
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
|
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test8() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "".as_bytes();
|
||||||
|
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::WrongAnswer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test9() {
|
||||||
|
let ans: &[u8] = "".as_bytes();
|
||||||
|
let out: &[u8] = "".as_bytes();
|
||||||
|
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test10() {
|
||||||
|
let ans: &[u8] = "Hello World!".as_bytes();
|
||||||
|
let out: &[u8] = "Hello World!\n".as_bytes();
|
||||||
|
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
108
src/runner.rs
108
src/runner.rs
@ -1,23 +1,30 @@
|
|||||||
|
extern crate nix;
|
||||||
|
|
||||||
use super::allow::{gen_rules, trace_syscall};
|
use super::allow::{gen_rules, trace_syscall};
|
||||||
use super::config::{STDERR_FILENAME, STDIN_FILENAME, STDOUT_FILENAME};
|
use super::config::{STDERR_FILENAME, STDIN_FILENAME, STDOUT_FILENAME};
|
||||||
use super::error::{errno_str, Error, Result};
|
use super::error::{errno_str, Error, Result};
|
||||||
use super::exec_args::ExecArgs;
|
use super::exec_args::ExecArgs;
|
||||||
use super::seccomp::*;
|
use super::seccomp::*;
|
||||||
|
use nix::unistd::close;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::File;
|
use std::fs::{remove_file, File};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io::{self, prelude::*, BufReader};
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::os::unix::io::IntoRawFd;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::mpsc;
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn MemoryUsage(fd: i32) -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Runner {
|
pub struct Runner {
|
||||||
pub pid: i32,
|
pub pid: i32,
|
||||||
@ -161,12 +168,18 @@ impl Runner {
|
|||||||
unsafe {
|
unsafe {
|
||||||
// 重定向文件描述符
|
// 重定向文件描述符
|
||||||
dup(STDIN_FILENAME, libc::STDIN_FILENO, libc::O_RDONLY, 0o644);
|
dup(STDIN_FILENAME, libc::STDIN_FILENO, libc::O_RDONLY, 0o644);
|
||||||
|
if Path::new(STDOUT_FILENAME).exists() {
|
||||||
|
remove_file(STDOUT_FILENAME).unwrap();
|
||||||
|
}
|
||||||
dup(
|
dup(
|
||||||
STDOUT_FILENAME,
|
STDOUT_FILENAME,
|
||||||
libc::STDOUT_FILENO,
|
libc::STDOUT_FILENO,
|
||||||
libc::O_CREAT | libc::O_RDWR,
|
libc::O_CREAT | libc::O_RDWR,
|
||||||
0o644,
|
0o644,
|
||||||
);
|
);
|
||||||
|
if Path::new(STDERR_FILENAME).exists() {
|
||||||
|
remove_file(STDERR_FILENAME).unwrap();
|
||||||
|
}
|
||||||
dup(
|
dup(
|
||||||
STDERR_FILENAME,
|
STDERR_FILENAME,
|
||||||
libc::STDERR_FILENO,
|
libc::STDERR_FILENO,
|
||||||
@ -180,7 +193,7 @@ impl Runner {
|
|||||||
panic!("How dare you!");
|
panic!("How dare you!");
|
||||||
}
|
}
|
||||||
// CPU 时间限制,粒度为 S
|
// CPU 时间限制,粒度为 S
|
||||||
rl.rlim_cur = (self.time_limit / 1000 + 1) as u64;
|
rl.rlim_cur = (self.time_limit as u64) / 1000 + 1;
|
||||||
rl.rlim_max = rl.rlim_cur + 1;
|
rl.rlim_max = rl.rlim_cur + 1;
|
||||||
if libc::setrlimit(libc::RLIMIT_CPU, &rl) != 0 {
|
if libc::setrlimit(libc::RLIMIT_CPU, &rl) != 0 {
|
||||||
eprintln!("setrlimit failure!");
|
eprintln!("setrlimit failure!");
|
||||||
@ -188,7 +201,8 @@ impl Runner {
|
|||||||
panic!("How dare you!");
|
panic!("How dare you!");
|
||||||
}
|
}
|
||||||
// 设置内存限制
|
// 设置内存限制
|
||||||
rl.rlim_cur = (self.memory_limit * 1024) as u64;
|
if self.memory_limit > 0 {
|
||||||
|
rl.rlim_cur = (self.memory_limit as u64) * 1024;
|
||||||
rl.rlim_max = rl.rlim_cur + 1024;
|
rl.rlim_max = rl.rlim_cur + 1024;
|
||||||
if libc::setrlimit(libc::RLIMIT_DATA, &rl) != 0 {
|
if libc::setrlimit(libc::RLIMIT_DATA, &rl) != 0 {
|
||||||
eprintln!("setrlimit failure!");
|
eprintln!("setrlimit failure!");
|
||||||
@ -200,6 +214,7 @@ impl Runner {
|
|||||||
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
||||||
panic!("How dare you!");
|
panic!("How dare you!");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if self.traceme {
|
if self.traceme {
|
||||||
// 设置 trace 模式
|
// 设置 trace 模式
|
||||||
libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0);
|
libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0);
|
||||||
@ -229,6 +244,7 @@ impl Runner {
|
|||||||
}
|
}
|
||||||
SeccompFilter::apply(filter.try_into().unwrap()).unwrap();
|
SeccompFilter::apply(filter.try_into().unwrap()).unwrap();
|
||||||
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
|
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
|
||||||
|
libc::kill(libc::getpid(), libc::SIGKILL);
|
||||||
}
|
}
|
||||||
panic!("How dare you!");
|
panic!("How dare you!");
|
||||||
}
|
}
|
||||||
@ -267,6 +283,17 @@ impl Runner {
|
|||||||
// from: https://www.hackerearth.com/practice/notes/vivekprakash/technical-diving-into-memory-used-by-a-program-in-online-judges/
|
// from: https://www.hackerearth.com/practice/notes/vivekprakash/technical-diving-into-memory-used-by-a-program-in-online-judges/
|
||||||
// VmRSS 为程序当前驻留在物理内存中的大小,对虚拟内存等无效
|
// VmRSS 为程序当前驻留在物理内存中的大小,对虚拟内存等无效
|
||||||
let mut vm_mem = 0;
|
let mut vm_mem = 0;
|
||||||
|
let status_file = format!("/proc/{}/status", pid);
|
||||||
|
let file = match File::open(status_file) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(_) => {
|
||||||
|
return judge_error(format!(
|
||||||
|
"open file `{}` failure!",
|
||||||
|
format!("/proc/{}/status", pid)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let status_fd = file.into_raw_fd();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.traceme {
|
if self.traceme {
|
||||||
@ -283,52 +310,15 @@ impl Runner {
|
|||||||
libc::ptrace(libc::PTRACE_SYSCALL, pid, 0, 0);
|
libc::ptrace(libc::PTRACE_SYSCALL, pid, 0, 0);
|
||||||
libc::waitpid(pid, &mut status, 0);
|
libc::waitpid(pid, &mut status, 0);
|
||||||
|
|
||||||
// 通过 /proc/pid/status 读取内存占用
|
let vmem = MemoryUsage(status_fd);
|
||||||
let status_file = format!("/proc/{}/status", pid);
|
if vm_mem < vmem {
|
||||||
debug!("{}", status_file);
|
vm_mem = vmem;
|
||||||
let file = match File::open(status_file) {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => {
|
|
||||||
return judge_error(format!(
|
|
||||||
"open file `{}` failure!",
|
|
||||||
format!("/proc/{}/status", pid)
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
// trace 模式下,如果检测到内存已经超出限制,则直接 kill & break
|
||||||
let reader = BufReader::new(file);
|
if vm_mem > self.memory_limit.into() {
|
||||||
let mut vm_data = 0;
|
debug!("MemoryLimitExceeded! break");
|
||||||
let mut vm_stk = 0;
|
libc::kill(pid, libc::SIGKILL);
|
||||||
for line in reader.lines() {
|
break;
|
||||||
let s = match line {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => return judge_error("readline failure!".to_string()),
|
|
||||||
};
|
|
||||||
if s.starts_with("VmData") {
|
|
||||||
let len = s.len() - 2;
|
|
||||||
let mem = &s[7..len].trim();
|
|
||||||
let mem = match mem.parse::<i64>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => return judge_error(format!("parse {} failure!", mem)),
|
|
||||||
};
|
|
||||||
if vm_data < mem {
|
|
||||||
vm_data = mem;
|
|
||||||
}
|
|
||||||
debug!("vm_data: {}", vm_data);
|
|
||||||
} else if s.starts_with("VmStk") {
|
|
||||||
let len = s.len() - 2;
|
|
||||||
let mem = &s[6..len].trim();
|
|
||||||
let mem = match mem.parse::<i64>() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => return judge_error(format!("parse {} failure!", mem)),
|
|
||||||
};
|
|
||||||
if vm_stk < mem {
|
|
||||||
vm_stk = mem;
|
|
||||||
}
|
|
||||||
debug!("vm_stk: {}", vm_stk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if vm_mem < vm_stk + vm_data {
|
|
||||||
vm_mem = vm_stk + vm_data;
|
|
||||||
}
|
}
|
||||||
debug!("vm_mem: {}", vm_mem);
|
debug!("vm_mem: {}", vm_mem);
|
||||||
|
|
||||||
@ -341,23 +331,25 @@ impl Runner {
|
|||||||
debug!("exited: {}", libc::WIFEXITED(status));
|
debug!("exited: {}", libc::WIFEXITED(status));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
debug!("exited: {}", libc::WIFEXITED(status));
|
// debug!("exited: {}", libc::WIFEXITED(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match close(status_fd) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => return judge_error("close status file failure!".to_string()),
|
||||||
|
};
|
||||||
let mut exit_code = 0;
|
let mut exit_code = 0;
|
||||||
let exited = unsafe { libc::WIFEXITED(status) };
|
let exited = libc::WIFEXITED(status);
|
||||||
if exited {
|
if exited {
|
||||||
exit_code = unsafe { libc::WEXITSTATUS(status) };
|
exit_code = libc::WEXITSTATUS(status);
|
||||||
}
|
}
|
||||||
let signal = unsafe {
|
let signal = if libc::WIFSIGNALED(status) {
|
||||||
if libc::WIFSIGNALED(status) {
|
|
||||||
libc::WTERMSIG(status)
|
libc::WTERMSIG(status)
|
||||||
} else if libc::WIFSTOPPED(status) {
|
} else if libc::WIFSTOPPED(status) {
|
||||||
libc::WSTOPSIG(status)
|
libc::WSTOPSIG(status)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// TODO: 添加 CGroup 的量度
|
// TODO: 添加 CGroup 的量度
|
||||||
let time_used = rusage.ru_utime.tv_sec * 1000
|
let time_used = rusage.ru_utime.tv_sec * 1000
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user