mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
输入数据方式改造
This commit is contained in:
parent
5a45a33eb7
commit
425ce0beaa
@ -1,5 +1,5 @@
|
|||||||
use crate::river::{JudgeResult, JudgeStatus};
|
|
||||||
use crate::river::JudgeResponse;
|
use crate::river::JudgeResponse;
|
||||||
|
use crate::river::{JudgeResult, JudgeStatus};
|
||||||
use libc::strerror;
|
use libc::strerror;
|
||||||
use std::ffi::{CStr, NulError, OsString};
|
use std::ffi::{CStr, NulError, OsString};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@ -16,8 +16,10 @@ pub enum Error {
|
|||||||
StringToCStringError(NulError),
|
StringToCStringError(NulError),
|
||||||
OsStringToStringError(OsString),
|
OsStringToStringError(OsString),
|
||||||
RemoveFileError(PathBuf),
|
RemoveFileError(PathBuf),
|
||||||
|
PathBufToStringError(PathBuf),
|
||||||
UnknownRequestData,
|
UnknownRequestData,
|
||||||
RequestDataNotFound,
|
RequestDataNotFound,
|
||||||
|
SyscallError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@ -36,6 +38,11 @@ pub fn errno_str(errno: Option<i32>) -> String {
|
|||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
|
Error::SyscallError(ref syscall) => {
|
||||||
|
let errno = io::Error::last_os_error().raw_os_error();
|
||||||
|
let reason = errno_str(errno);
|
||||||
|
write!(f, "SyscallError: `{}` {}", syscall, reason)
|
||||||
|
}
|
||||||
_ => write!(f, "{:?}", self),
|
_ => write!(f, "{:?}", self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use super::error::{Error, Result};
|
use super::error::{Error, Result};
|
||||||
use super::process::{Process, ProcessStatus};
|
use super::process::{Process, ProcessStatus};
|
||||||
use crate::river::Language;
|
use crate::river::Language;
|
||||||
use crate::river::{JudgeResult, JudgeStatus, CompileData, JudgeData};
|
use crate::river::{CompileData, JudgeData, JudgeResult, JudgeStatus};
|
||||||
use crate::river::{JudgeRequest, JudgeResponse};
|
use crate::river::{JudgeRequest, JudgeResponse};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
@ -23,7 +23,11 @@ impl JudgeResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn judger(request: &JudgeRequest, data: &JudgeData, path: &Path) -> Result<JudgeResponse> {
|
pub async fn judger(
|
||||||
|
request: &JudgeRequest,
|
||||||
|
data: &JudgeData,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<JudgeResponse> {
|
||||||
let mut resp = JudgeResponse::new();
|
let mut resp = JudgeResponse::new();
|
||||||
let cmd = match Language::from_i32(request.language) {
|
let cmd = match Language::from_i32(request.language) {
|
||||||
Some(Language::C) => "./a.out",
|
Some(Language::C) => "./a.out",
|
||||||
@ -35,12 +39,13 @@ pub async fn judger(request: &JudgeRequest, data: &JudgeData, path: &Path) -> Re
|
|||||||
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();
|
let mut process = Process::new(cmd.to_string(), path.to_path_buf());
|
||||||
process.cmd = cmd.to_string();
|
|
||||||
process.workdir = 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)?;
|
||||||
|
|
||||||
// TODO: 使用内存流替换,尽可能减少文件读写与复制
|
// TODO: 使用内存流替换,尽可能减少文件读写与复制
|
||||||
// 写入输入文件
|
// 写入输入文件
|
||||||
let in_file = path.join("stdin.txt");
|
let in_file = path.join("stdin.txt");
|
||||||
@ -66,7 +71,11 @@ pub async fn judger(request: &JudgeRequest, data: &JudgeData, path: &Path) -> Re
|
|||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn compile(request: &JudgeRequest, data: &CompileData, path: &Path) -> Result<JudgeResponse> {
|
pub async fn compile(
|
||||||
|
request: &JudgeRequest,
|
||||||
|
data: &CompileData,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<JudgeResponse> {
|
||||||
let mut resp = JudgeResponse::new();
|
let mut resp = JudgeResponse::new();
|
||||||
resp.status = JudgeStatus::Ended as i32;
|
resp.status = JudgeStatus::Ended as i32;
|
||||||
// 写入代码
|
// 写入代码
|
||||||
@ -84,7 +93,6 @@ pub async fn compile(request: &JudgeRequest, data: &CompileData, path: &Path) ->
|
|||||||
return Err(Error::FileWriteError(e));
|
return Err(Error::FileWriteError(e));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut process = Process::new();
|
|
||||||
let cmd = match Language::from_i32(request.language) {
|
let cmd = match Language::from_i32(request.language) {
|
||||||
Some(Language::C) => "/usr/bin/gcc main.c -o a.out -Wall -O2 -std=c99 --static",
|
Some(Language::C) => "/usr/bin/gcc main.c -o a.out -Wall -O2 -std=c99 --static",
|
||||||
Some(Language::Cpp) => "/usr/bin/g++ main.cpp -O2 -Wall --static -o a.out --std=gnu++17",
|
Some(Language::Cpp) => "/usr/bin/g++ main.cpp -O2 -Wall --static -o a.out --std=gnu++17",
|
||||||
@ -97,8 +105,7 @@ pub async fn compile(request: &JudgeRequest, data: &CompileData, path: &Path) ->
|
|||||||
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)),
|
||||||
};
|
};
|
||||||
process.cmd = cmd.to_string();
|
let mut process = Process::new(cmd.to_string(), path.to_path_buf());
|
||||||
process.workdir = path.to_path_buf();
|
|
||||||
// 编译的资源限制为固定的
|
// 编译的资源限制为固定的
|
||||||
process.time_limit = 10000;
|
process.time_limit = 10000;
|
||||||
process.memory_limit = 64 * 1024;
|
process.memory_limit = 64 * 1024;
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
use super::error::{errno_str, Error, Result};
|
use super::error::{errno_str, Error, Result};
|
||||||
use libc;
|
use libc;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::ffi::c_void;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::os::raw::c_char;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
@ -22,40 +24,91 @@ pub struct Process {
|
|||||||
pub time_limit: i32,
|
pub time_limit: i32,
|
||||||
pub memory_limit: i32,
|
pub memory_limit: i32,
|
||||||
pub stdin_file: Option<PathBuf>,
|
pub stdin_file: Option<PathBuf>,
|
||||||
|
stdin_fd: Option<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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn new() -> Process {
|
pub fn new(cmd: String, workdir: PathBuf) -> Process {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
Process {
|
Process {
|
||||||
pid: -1,
|
pid: -1,
|
||||||
time_limit: -1,
|
time_limit: -1,
|
||||||
memory_limit: -1,
|
memory_limit: -1,
|
||||||
stdin_file: None,
|
stdin_file: None,
|
||||||
cmd: "".to_string(),
|
stdin_fd: None,
|
||||||
workdir: PathBuf::from(""),
|
cmd: cmd,
|
||||||
|
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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub 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(dead_code)]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
// 为进程设置 stdin 的数据
|
// 为进程设置 stdin 的数据
|
||||||
pub fn set_stdin(in_data: &Vec<u8>) {
|
pub fn set_stdin(&mut self, in_data: &Vec<u8>) -> Result<()> {
|
||||||
// TODO
|
let memfile = self.workdir_str()?;
|
||||||
|
// 打开内存文件
|
||||||
|
let fd = unsafe {
|
||||||
|
libc::shm_open(
|
||||||
|
CString::new(memfile).unwrap().as_ptr(),
|
||||||
|
libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if fd <= 0 {
|
||||||
|
return Err(Error::SyscallError("shm_open".to_string()));
|
||||||
|
}
|
||||||
|
self.stdin_fd = Some(fd);
|
||||||
|
// 扩充内存到数据文件大小
|
||||||
|
if unsafe { libc::ftruncate(fd, in_data.len() as i64) } < 0 {
|
||||||
|
return Err(Error::SyscallError("ftruncate".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制数据到创建的内存中
|
||||||
|
unsafe {
|
||||||
|
let ptr = libc::mmap(
|
||||||
|
std::ptr::null_mut() as *mut c_void,
|
||||||
|
in_data.len(),
|
||||||
|
libc::PROT_WRITE,
|
||||||
|
libc::MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
if ptr == libc::MAP_FAILED {
|
||||||
|
return Err(Error::SyscallError("mmap".to_string()));
|
||||||
|
}
|
||||||
|
libc::strcpy(ptr as *mut c_char, in_data.as_ptr() as *const i8);
|
||||||
|
// 复制完要记得 munmap,否则会造成无法回收的内存泄露
|
||||||
|
if libc::munmap(ptr, in_data.len()) < 0 {
|
||||||
|
return Err(Error::SyscallError("munmap".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
// 从 stdout 中读取指定长度的内容
|
// 从 stdout 中读取指定长度的内容
|
||||||
pub fn read_stdout(len: i32) {
|
pub fn read_stdout(&mut self, len: i32) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +137,15 @@ impl Drop for Process {
|
|||||||
libc::waitpid(self.pid, &mut status, 0);
|
libc::waitpid(self.pid, &mut status, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果设置了 stdin 数据,则需要释放对应的内存
|
||||||
|
if let Some(_) = self.stdin_fd {
|
||||||
|
// 如果 stdin_fd 有值,则说明 pathbuf 的转换一定没问题,否则上面也不会转换成功
|
||||||
|
let memfile = self.workdir.clone().into_os_string().into_string().unwrap();
|
||||||
|
unsafe {
|
||||||
|
libc::shm_unlink(CString::new(memfile).unwrap().as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,9 +314,13 @@ impl Process {
|
|||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
// 重定向文件描述符
|
// 重定向文件描述符
|
||||||
if let Some(file) = &self.stdin_file {
|
if let Some(fd) = self.stdin_fd {
|
||||||
let filename = file.to_str().unwrap();
|
if libc::dup2(fd, libc::STDIN_FILENO) < 0 {
|
||||||
dup(&filename, libc::STDIN_FILENO, libc::O_RDONLY, 0o644)
|
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(
|
||||||
"stdout.txt",
|
"stdout.txt",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user