mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
Update
This commit is contained in:
parent
425ce0beaa
commit
177e228ec2
@ -7,6 +7,7 @@ use std::io;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
CreateTempDirError(io::Error),
|
CreateTempDirError(io::Error),
|
||||||
|
|||||||
@ -39,27 +39,16 @@ pub async fn judger(
|
|||||||
Some(Language::Go) => "./a.out",
|
Some(Language::Go) => "./a.out",
|
||||||
None => return Err(Error::LanguageNotFound(request.language)),
|
None => return Err(Error::LanguageNotFound(request.language)),
|
||||||
};
|
};
|
||||||
let mut process = Process::new(cmd.to_string(), path.to_path_buf());
|
let mut process = Process::new(cmd.to_string(), path.to_path_buf())?;
|
||||||
process.time_limit = data.time_limit;
|
process.time_limit = data.time_limit;
|
||||||
process.memory_limit = data.memory_limit;
|
process.memory_limit = data.memory_limit;
|
||||||
|
|
||||||
// 设置输入数据
|
// 设置输入数据
|
||||||
process.set_stdin(&data.in_data)?;
|
process.set_stdin(&data.in_data)?;
|
||||||
|
|
||||||
// TODO: 使用内存流替换,尽可能减少文件读写与复制
|
// 开始执行并等待返回结果
|
||||||
// 写入输入文件
|
|
||||||
let in_file = path.join("stdin.txt");
|
|
||||||
if let Err(e) = fs::write(in_file.clone(), data.in_data.clone()).await {
|
|
||||||
return Err(Error::FileWriteError(e));
|
|
||||||
};
|
|
||||||
process.stdin_file = Some(in_file.clone());
|
|
||||||
|
|
||||||
let status = process.await?;
|
let status = process.await?;
|
||||||
|
|
||||||
if let Err(_) = fs::remove_file(in_file.clone()).await {
|
|
||||||
return Err(Error::RemoveFileError(in_file.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.status = JudgeStatus::Ended as i32;
|
resp.status = JudgeStatus::Ended as i32;
|
||||||
// TODO: 根据返回值等判断 tle、mle、re 等状态
|
// TODO: 根据返回值等判断 tle、mle、re 等状态
|
||||||
// TODO: 对比答案,检查结果
|
// TODO: 对比答案,检查结果
|
||||||
@ -105,7 +94,7 @@ pub async fn compile(
|
|||||||
Some(Language::Go) => "/usr/bin/go build -ldflags \"-s -w\" main.go",
|
Some(Language::Go) => "/usr/bin/go build -ldflags \"-s -w\" main.go",
|
||||||
None => return Err(Error::LanguageNotFound(request.language)),
|
None => return Err(Error::LanguageNotFound(request.language)),
|
||||||
};
|
};
|
||||||
let mut process = Process::new(cmd.to_string(), path.to_path_buf());
|
let mut process = Process::new(cmd.to_string(), path.to_path_buf())?;
|
||||||
// 编译的资源限制为固定的
|
// 编译的资源限制为固定的
|
||||||
process.time_limit = 10000;
|
process.time_limit = 10000;
|
||||||
process.memory_limit = 64 * 1024;
|
process.memory_limit = 64 * 1024;
|
||||||
@ -113,7 +102,7 @@ pub async fn compile(
|
|||||||
let status = process.await?;
|
let status = process.await?;
|
||||||
if status.exit_code != 0 {
|
if status.exit_code != 0 {
|
||||||
resp.set_process_status(&status);
|
resp.set_process_status(&status);
|
||||||
resp.errmsg = format!("{}{}", status.stdout, status.stderr);
|
// resp.errmsg = format!("{}{}", status.stdout, status.stderr);
|
||||||
resp.result = JudgeResult::CompileError as i32;
|
resp.result = JudgeResult::CompileError as i32;
|
||||||
}
|
}
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
|
|||||||
142
src/process.rs
142
src/process.rs
@ -3,7 +3,6 @@ use libc;
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -23,48 +22,74 @@ pub struct Process {
|
|||||||
pub workdir: PathBuf,
|
pub workdir: PathBuf,
|
||||||
pub time_limit: i32,
|
pub time_limit: i32,
|
||||||
pub memory_limit: i32,
|
pub memory_limit: i32,
|
||||||
pub stdin_file: Option<PathBuf>,
|
|
||||||
stdin_fd: Option<i32>,
|
stdin_fd: Option<i32>,
|
||||||
|
stdout_fd: i32,
|
||||||
|
stderr_fd: i32,
|
||||||
pub cmd: String,
|
pub cmd: String,
|
||||||
tx: Arc<Mutex<mpsc::Sender<ProcessStatus>>>,
|
tx: Arc<Mutex<mpsc::Sender<ProcessStatus>>>,
|
||||||
rx: Arc<Mutex<mpsc::Receiver<ProcessStatus>>>,
|
rx: Arc<Mutex<mpsc::Receiver<ProcessStatus>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn path_buf_str(path_buf: &PathBuf) -> Result<String> {
|
||||||
|
let file = match path_buf.file_stem() {
|
||||||
|
Some(stem) => match stem.to_str() {
|
||||||
|
Some(val) => val.to_string(),
|
||||||
|
None => return Err(Error::PathBufToStringError(path_buf.clone())),
|
||||||
|
},
|
||||||
|
None => return Err(Error::PathBufToStringError(path_buf.clone())),
|
||||||
|
};
|
||||||
|
Ok(file)
|
||||||
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn new(cmd: String, workdir: PathBuf) -> Process {
|
pub fn new(cmd: String, workdir: PathBuf) -> Result<Process> {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
Process {
|
|
||||||
|
let memfile = path_buf_str(&workdir)?;
|
||||||
|
let outfile = match CString::new(format!("{}{}", "stdout", memfile)) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => return Err(Error::StringToCStringError(e)),
|
||||||
|
};
|
||||||
|
let errfile = match CString::new(format!("{}{}", "stderr", memfile)) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => return Err(Error::StringToCStringError(e)),
|
||||||
|
};
|
||||||
|
let stdout_fd = unsafe {
|
||||||
|
libc::shm_open(
|
||||||
|
outfile.as_ptr(),
|
||||||
|
libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let stderr_fd = unsafe {
|
||||||
|
libc::shm_open(
|
||||||
|
errfile.as_ptr(),
|
||||||
|
libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Process {
|
||||||
pid: -1,
|
pid: -1,
|
||||||
time_limit: -1,
|
time_limit: -1,
|
||||||
memory_limit: -1,
|
memory_limit: -1,
|
||||||
stdin_file: None,
|
|
||||||
stdin_fd: None,
|
stdin_fd: None,
|
||||||
|
stdout_fd: stdout_fd,
|
||||||
|
stderr_fd: stderr_fd,
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
workdir: workdir,
|
workdir: workdir,
|
||||||
tx: Arc::new(Mutex::new(tx)),
|
tx: Arc::new(Mutex::new(tx)),
|
||||||
rx: Arc::new(Mutex::new(rx)),
|
rx: Arc::new(Mutex::new(rx)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
fn set_pid(&mut self, pid: i32) {
|
fn set_pid(&mut self, pid: i32) {
|
||||||
self.pid = pid;
|
self.pid = pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workdir_str(&self) -> Result<String> {
|
|
||||||
let file = match self.workdir.file_stem() {
|
|
||||||
Some(stem) => match stem.to_str() {
|
|
||||||
Some(val) => val.to_string(),
|
|
||||||
None => return Err(Error::PathBufToStringError(self.workdir.clone())),
|
|
||||||
},
|
|
||||||
None => return Err(Error::PathBufToStringError(self.workdir.clone())),
|
|
||||||
};
|
|
||||||
Ok(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
// 为进程设置 stdin 的数据
|
// 为进程设置 stdin 的数据
|
||||||
pub fn set_stdin(&mut self, in_data: &Vec<u8>) -> Result<()> {
|
pub fn set_stdin(&mut self, in_data: &Vec<u8>) -> Result<()> {
|
||||||
let memfile = self.workdir_str()?;
|
let memfile = path_buf_str(&self.workdir)?;
|
||||||
|
let memfile = format!("{}{}", "stdin", memfile);
|
||||||
// 打开内存文件
|
// 打开内存文件
|
||||||
let fd = unsafe {
|
let fd = unsafe {
|
||||||
libc::shm_open(
|
libc::shm_open(
|
||||||
@ -315,25 +340,10 @@ impl Process {
|
|||||||
unsafe {
|
unsafe {
|
||||||
// 重定向文件描述符
|
// 重定向文件描述符
|
||||||
if let Some(fd) = self.stdin_fd {
|
if let Some(fd) = self.stdin_fd {
|
||||||
if libc::dup2(fd, libc::STDIN_FILENO) < 0 {
|
dup(fd, libc::STDIN_FILENO);
|
||||||
let err = io::Error::last_os_error().raw_os_error();
|
|
||||||
eprintln!("dup2 failure!");
|
|
||||||
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
|
||||||
panic!(errno_str(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dup(
|
dup(self.stdout_fd, libc::STDOUT_FILENO);
|
||||||
"stdout.txt",
|
dup(self.stderr_fd, libc::STDERR_FILENO);
|
||||||
libc::STDOUT_FILENO,
|
|
||||||
libc::O_CREAT | libc::O_RDWR,
|
|
||||||
0o644,
|
|
||||||
);
|
|
||||||
dup(
|
|
||||||
"stderr.txt",
|
|
||||||
libc::STDERR_FILENO,
|
|
||||||
libc::O_CREAT | libc::O_RDWR,
|
|
||||||
0o644,
|
|
||||||
);
|
|
||||||
// 墙上时钟限制
|
// 墙上时钟限制
|
||||||
if setitimer(ITIMER_REAL, &rt, ptr::null_mut()) == -1 {
|
if setitimer(ITIMER_REAL, &rt, ptr::null_mut()) == -1 {
|
||||||
eprintln!("setitimer failure!");
|
eprintln!("setitimer failure!");
|
||||||
@ -367,17 +377,8 @@ impl Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn dup(filename: &str, to: libc::c_int, flag: libc::c_int, mode: libc::c_int) {
|
unsafe fn dup(from: libc::c_int, to: libc::c_int) {
|
||||||
let filename_str = CString::new(filename).unwrap();
|
if libc::dup2(from, to) < 0 {
|
||||||
let filename = filename_str.as_ptr();
|
|
||||||
let fd = libc::open(filename, flag, mode);
|
|
||||||
if fd < 0 {
|
|
||||||
let err = io::Error::last_os_error().raw_os_error();
|
|
||||||
eprintln!("open failure!");
|
|
||||||
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
|
||||||
panic!(errno_str(err));
|
|
||||||
}
|
|
||||||
if libc::dup2(fd, to) < 0 {
|
|
||||||
let err = io::Error::last_os_error().raw_os_error();
|
let err = io::Error::last_os_error().raw_os_error();
|
||||||
eprintln!("dup2 failure!");
|
eprintln!("dup2 failure!");
|
||||||
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
|
||||||
@ -449,20 +450,6 @@ impl Process {
|
|||||||
// How dare you!
|
// How dare you!
|
||||||
Err(_) => panic!("How dare you!"),
|
Err(_) => panic!("How dare you!"),
|
||||||
};
|
};
|
||||||
let stdout = match fs::read_to_string(&self.workdir.join("stdout.txt")) {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => panic!("How dare you!"),
|
|
||||||
};
|
|
||||||
if let Err(_) = fs::remove_file(&self.workdir.join("stdout.txt")) {
|
|
||||||
panic!("How dare you!");
|
|
||||||
}
|
|
||||||
let stderr = match fs::read_to_string(&self.workdir.join("stderr.txt")) {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => panic!("How dare you!"),
|
|
||||||
};
|
|
||||||
if let Err(_) = fs::remove_file(&self.workdir.join("stderr.txt")) {
|
|
||||||
panic!("How dare you!");
|
|
||||||
}
|
|
||||||
return ProcessStatus {
|
return ProcessStatus {
|
||||||
rusage: rusage,
|
rusage: rusage,
|
||||||
exit_code: exit_code,
|
exit_code: exit_code,
|
||||||
@ -471,8 +458,6 @@ impl Process {
|
|||||||
time_used: time_used,
|
time_used: time_used,
|
||||||
memory_used: memory_used,
|
memory_used: memory_used,
|
||||||
real_time_used: real_time_used,
|
real_time_used: real_time_used,
|
||||||
stdout: stdout,
|
|
||||||
stderr: stderr,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,6 +471,29 @@ pub struct ProcessStatus {
|
|||||||
pub time_used: i64,
|
pub time_used: i64,
|
||||||
pub memory_used: i64,
|
pub memory_used: i64,
|
||||||
pub real_time_used: u128,
|
pub real_time_used: u128,
|
||||||
pub stdout: String,
|
}
|
||||||
pub stderr: String,
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::process::Process;
|
||||||
|
use std::fs;
|
||||||
|
use tempfile::tempdir_in;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello() {
|
||||||
|
let s = String::from("hello");
|
||||||
|
let bytes = s.into_bytes();
|
||||||
|
assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn run() {
|
||||||
|
let cmd = String::from("/bin/echo hello world");
|
||||||
|
let pwd = tempdir_in("./runner").unwrap().into_path();
|
||||||
|
let path = pwd.to_str().unwrap();
|
||||||
|
let process = Process::new(cmd, pwd.clone()).unwrap();
|
||||||
|
let status = process.await.unwrap();
|
||||||
|
fs::remove_dir_all(path).unwrap();
|
||||||
|
assert_eq!(status.exit_code, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user