mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
commit
0a931fbc27
@ -1,24 +0,0 @@
|
||||
use std::result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Error(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct Cgroup {
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
impl Cgroup {
|
||||
pub fn new(pid: u32) -> Result<Self> {
|
||||
return Ok(Cgroup { pid });
|
||||
}
|
||||
pub fn attach(&self) -> Result<()> {
|
||||
if self.pid == 0 {
|
||||
return Err(Error::Error("Hello World!".to_string()));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,11 @@
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::result;
|
||||
use yaml_rust::{ScanError, Yaml, YamlLoader};
|
||||
use std::io;
|
||||
use yaml_rust::{Yaml, YamlLoader};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
YamlScanError(ScanError),
|
||||
YamlParseError(String),
|
||||
ReadFileError,
|
||||
LanguageNotFound(String),
|
||||
}
|
||||
use super::error::{Error, Result};
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LanguageConfig {
|
||||
pub language: String,
|
||||
pub version: String,
|
||||
@ -20,6 +13,12 @@ pub struct LanguageConfig {
|
||||
pub run_command: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for LanguageConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} {}", self.language, self.version)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub languages: Vec<LanguageConfig>,
|
||||
}
|
||||
@ -28,7 +27,7 @@ impl Config {
|
||||
pub fn language_config_from_name(&self, name: &str) -> Result<LanguageConfig> {
|
||||
for language in &self.languages {
|
||||
if language.language == name {
|
||||
return Ok(language.clone())
|
||||
return Ok(language.clone());
|
||||
}
|
||||
}
|
||||
Err(Error::LanguageNotFound(name.to_string()))
|
||||
@ -101,7 +100,12 @@ impl Config {
|
||||
pub fn load_from_file(filename: &str) -> Result<Config> {
|
||||
let contents = match fs::read_to_string(filename) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Err(Error::ReadFileError),
|
||||
Err(_) => {
|
||||
return Err(Error::ReadFileError(
|
||||
filename.to_string(),
|
||||
io::Error::last_os_error().raw_os_error(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let config = Config::load_yaml(&contents)?;
|
||||
Ok(config)
|
||||
|
||||
@ -3,6 +3,14 @@ languages:
|
||||
version: 7.5.0
|
||||
compile_command: /usr/bin/gcc {{filename}} -o a.out
|
||||
run_command: ./a.out
|
||||
- language: cpp
|
||||
version: 7.5.0
|
||||
compile_command: /usr/bin/g++ {{filename}} -o a.out
|
||||
run_command: ./a.out
|
||||
- language: bash
|
||||
version: 4.4.20
|
||||
compile_command: "/bin/echo Hello World!"
|
||||
run_command: /bin/bash {{filename}}
|
||||
- language: python
|
||||
version: 3.6.9
|
||||
compile_command: /usr/bin/python3 -m compileall {{filename}}
|
||||
|
||||
53
src/error.rs
Normal file
53
src/error.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use handlebars::RenderError;
|
||||
use libc::strerror;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::NulError;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
use yaml_rust::ScanError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
YamlScanError(ScanError),
|
||||
YamlParseError(String),
|
||||
LanguageNotFound(String),
|
||||
ReadFileError(String, Option<i32>),
|
||||
|
||||
UnknownJudgeType(String),
|
||||
PathJoinError,
|
||||
|
||||
StringToCStringError(NulError),
|
||||
TemplateRenderError(RenderError),
|
||||
LanguageConfigError(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::YamlScanError(ref err) => {
|
||||
let _ = write!(f, "YamlScanError: ");
|
||||
err.fmt(f)
|
||||
}
|
||||
Error::YamlParseError(ref err) => write!(f, "YamlParseError: {}", err),
|
||||
Error::LanguageNotFound(ref lang) => write!(f, "LanguageNotFound: {}", lang),
|
||||
Error::ReadFileError(ref filename, errno) => {
|
||||
let reason = match errno {
|
||||
Some(no) => {
|
||||
let stre = unsafe { strerror(no) };
|
||||
let c_str: &CStr = unsafe { CStr::from_ptr(stre) };
|
||||
c_str.to_str().unwrap()
|
||||
}
|
||||
_ => "Unknown Error!",
|
||||
};
|
||||
write!(f, "ReadFileError: `{}` {}", filename, reason)
|
||||
}
|
||||
Error::UnknownJudgeType(ref judge_type) => {
|
||||
write!(f, "UnknownJudgeType: {}", judge_type)
|
||||
}
|
||||
Error::PathJoinError => write!(f, "PathJoinError"),
|
||||
_ => write!(f, "{:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,48 +1,33 @@
|
||||
use std::ffi::CString;
|
||||
use std::ffi::NulError;
|
||||
use std::fs;
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
use std::result;
|
||||
use yaml_rust::{ScanError, Yaml, YamlLoader};
|
||||
|
||||
use super::config;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
StringToCStringError(NulError),
|
||||
ReadFileError,
|
||||
PathJoinError,
|
||||
YamlScanError(ScanError),
|
||||
YamlParseError(String),
|
||||
UnknownJudgeType(String),
|
||||
LanguageNotFound(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
use super::error::{Error, Result};
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use yaml_rust::{Yaml, YamlLoader};
|
||||
|
||||
pub struct TestCase {
|
||||
pub index: u32,
|
||||
pub input_file: String,
|
||||
pub answer_file: String,
|
||||
pub cpu_time_limit: u32,
|
||||
pub real_time_limit: u32,
|
||||
pub memory_limit: u32,
|
||||
pub result: Option<TestCaseResult>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TestCaseResult {
|
||||
Accepted,
|
||||
CompileError(String),
|
||||
WrongAnswer,
|
||||
RuntimeError(String),
|
||||
SystemError(String),
|
||||
}
|
||||
|
||||
pub struct JudgeCode {
|
||||
pub file: String,
|
||||
pub language: config::LanguageConfig,
|
||||
impl fmt::Display for TestCase {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"index: {}
|
||||
input_file: {}
|
||||
answer_file: {}
|
||||
time_limit: {}
|
||||
memory_limit: {}
|
||||
",
|
||||
self.index, self.input_file, self.answer_file, self.cpu_time_limit, self.memory_limit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum JudgeType {
|
||||
@ -50,30 +35,20 @@ pub enum JudgeType {
|
||||
Special,
|
||||
}
|
||||
|
||||
pub struct TestConfig {
|
||||
pub default_time_limit: u32,
|
||||
pub default_real_time_limit: u32,
|
||||
pub default_memory_limit: u32,
|
||||
pub struct Code {
|
||||
pub file: String,
|
||||
pub language: config::LanguageConfig,
|
||||
}
|
||||
|
||||
pub struct JudgeConfig {
|
||||
pub tests: Vec<TestCase>,
|
||||
pub judge_type: JudgeType,
|
||||
pub extra_files: Vec<String>,
|
||||
pub code: JudgeCode,
|
||||
pub code: Code,
|
||||
}
|
||||
|
||||
pub struct JudgeConfigs {
|
||||
pub exec_file: String,
|
||||
pub exec_args: Vec<String>,
|
||||
pub test_cases: Vec<TestCase>,
|
||||
}
|
||||
|
||||
pub struct ExecArgs {
|
||||
pub pathname: *const libc::c_char,
|
||||
pub argv: *const *const libc::c_char,
|
||||
pub envp: *const *const libc::c_char,
|
||||
}
|
||||
|
||||
impl JudgeConfigs {
|
||||
fn load_yaml(global_config: &config::Config, yaml: &str) -> Result<TestConfig> {
|
||||
impl JudgeConfig {
|
||||
fn load_yaml(global_config: &config::Config, yaml: &str) -> Result<JudgeConfig> {
|
||||
let docs = match YamlLoader::load_from_str(yaml) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(Error::YamlScanError(err)),
|
||||
@ -89,15 +64,6 @@ impl JudgeConfigs {
|
||||
))
|
||||
}
|
||||
};
|
||||
let default_real_time_limit = match &doc["real_time_limit"] {
|
||||
Yaml::Integer(value) => value.clone() as u32,
|
||||
Yaml::BadValue => default_time_limit * 2 + 5000,
|
||||
_ => {
|
||||
return Err(Error::YamlParseError(
|
||||
"解析错误,real_time_limit 字段的类型应该为 Integer".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let default_memory_limit = match &doc["memory_limit"] {
|
||||
Yaml::Integer(value) => value.clone() as u32,
|
||||
Yaml::BadValue => 65535,
|
||||
@ -121,6 +87,8 @@ impl JudgeConfigs {
|
||||
}
|
||||
};
|
||||
let mut tests: Vec<TestCase> = vec![];
|
||||
// Yaml 解析库没有实现 enumerate 方法,因此此处使用 index 进行计数
|
||||
let mut index = 1;
|
||||
for case in test_cases {
|
||||
let cpu_time_limit = match &case["time_limit"] {
|
||||
Yaml::Integer(value) => value.clone() as u32,
|
||||
@ -176,13 +144,14 @@ impl JudgeConfigs {
|
||||
}
|
||||
};
|
||||
tests.push(TestCase {
|
||||
index,
|
||||
cpu_time_limit,
|
||||
real_time_limit,
|
||||
memory_limit,
|
||||
result: None,
|
||||
input_file,
|
||||
answer_file,
|
||||
});
|
||||
index += 1;
|
||||
}
|
||||
let judge_type = match &doc["judge_type"] {
|
||||
Yaml::String(value) => {
|
||||
@ -269,7 +238,7 @@ impl JudgeConfigs {
|
||||
))
|
||||
}
|
||||
};
|
||||
JudgeCode { file, language }
|
||||
Code { file, language }
|
||||
}
|
||||
Yaml::BadValue => {
|
||||
return Err(Error::YamlParseError(
|
||||
@ -283,20 +252,14 @@ impl JudgeConfigs {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TestConfig {
|
||||
default_time_limit,
|
||||
default_real_time_limit,
|
||||
default_memory_limit,
|
||||
Ok(JudgeConfig {
|
||||
tests,
|
||||
judge_type,
|
||||
extra_files,
|
||||
code,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 读取评测文件夹
|
||||
*/
|
||||
pub fn load(global_config: &config::Config, path: &str) -> Result<JudgeConfigs> {
|
||||
pub fn load(global_config: &config::Config, path: &str) -> Result<JudgeConfig> {
|
||||
let dir = Path::new(&path);
|
||||
|
||||
let config = dir.join("config.yml");
|
||||
@ -306,78 +269,13 @@ impl JudgeConfigs {
|
||||
};
|
||||
let config = match fs::read_to_string(&config) {
|
||||
Ok(value) => value,
|
||||
Err(_) => return Err(Error::ReadFileError),
|
||||
};
|
||||
let _config = JudgeConfigs::load_yaml(global_config, &config)?;
|
||||
|
||||
Ok(JudgeConfigs {
|
||||
exec_file: "".to_string(),
|
||||
exec_args: vec![],
|
||||
test_cases: vec![],
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 为 exec 函数生成参数
|
||||
* 涉及到 Rust 到 C 的内存转换,此过程是内存不安全的
|
||||
* 请务必手动清理内存,或者仅在马上要执行 exec 的位置执行此函数,以便由操作系统自动回收内存
|
||||
*/
|
||||
pub unsafe fn exec_args(&self) -> Result<ExecArgs> {
|
||||
let exec_file = match CString::new(self.exec_file.clone()) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||
};
|
||||
let exec_file_ptr = exec_file.as_ptr();
|
||||
let mut exec_args: Vec<*const libc::c_char> = vec![];
|
||||
for item in self.exec_args.iter() {
|
||||
let cstr = match CString::new(item.clone()) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||
};
|
||||
let cptr = cstr.as_ptr();
|
||||
// 需要使用 mem::forget 来标记
|
||||
// 否则在此次循环结束后,cstr 就会被回收,后续 exec 函数无法通过指针获取到字符串内容
|
||||
mem::forget(cstr);
|
||||
exec_args.push(cptr);
|
||||
}
|
||||
// argv 与 envp 的参数需要使用 NULL 来标记结束
|
||||
exec_args.push(ptr::null());
|
||||
let exec_args_ptr: *const *const libc::c_char =
|
||||
exec_args.as_ptr() as *const *const libc::c_char;
|
||||
let env: Vec<*const libc::c_char> = vec![ptr::null()];
|
||||
let env_ptr = env.as_ptr() as *const *const libc::c_char;
|
||||
mem::forget(env);
|
||||
mem::forget(exec_file);
|
||||
mem::forget(exec_args);
|
||||
|
||||
Ok(ExecArgs {
|
||||
pathname: exec_file_ptr,
|
||||
argv: exec_args_ptr,
|
||||
envp: env_ptr,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_base() {
|
||||
let run_args = JudgeConfigs {
|
||||
exec_file: "/bin/echo".to_string(),
|
||||
exec_args: vec![
|
||||
"/bin/echo".to_string(),
|
||||
"Hello".to_string(),
|
||||
"World".to_string(),
|
||||
],
|
||||
test_cases: vec![],
|
||||
};
|
||||
unsafe {
|
||||
let pid = libc::fork();
|
||||
if pid == 0 {
|
||||
let exec_args = run_args.exec_args().unwrap();
|
||||
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
|
||||
Err(_) => {
|
||||
return Err(Error::ReadFileError(
|
||||
path.to_string(),
|
||||
io::Error::last_os_error().raw_os_error(),
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
JudgeConfig::load_yaml(global_config, &config)
|
||||
}
|
||||
}
|
||||
99
src/main.rs
99
src/main.rs
@ -1,21 +1,12 @@
|
||||
extern crate libc;
|
||||
|
||||
use handlebars::Handlebars;
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
|
||||
mod config;
|
||||
mod runner;
|
||||
mod error;
|
||||
mod judger;
|
||||
mod process;
|
||||
mod result;
|
||||
|
||||
fn main() {
|
||||
let mut handlebars = Handlebars::new();
|
||||
let source = "hello {{world}}";
|
||||
let _ = handlebars.register_template_string("t1", source);
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("hello".to_string(), "你好".to_string());
|
||||
data.insert("world".to_string(), "世界!".to_string());
|
||||
println!("{}", handlebars.render("t1", &data).unwrap());
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let config = if args.len() > 1 {
|
||||
config::Config::load_from_file(&args[1])
|
||||
@ -25,85 +16,21 @@ fn main() {
|
||||
let config = match config {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
eprintln!("{:?}", err);
|
||||
return
|
||||
eprintln!("{}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let language = config.language_config_from_name("python");
|
||||
println!("{}", language.unwrap());
|
||||
|
||||
let _ = match runner::JudgeConfigs::load(&config, "example") {
|
||||
let judge_config = match judger::JudgeConfig::load(&config, "example") {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
eprintln!("{:?}", err);
|
||||
return
|
||||
eprintln!("{}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
println!("{}", judge_config.code.language);
|
||||
|
||||
process();
|
||||
}
|
||||
|
||||
fn process() {
|
||||
let pid;
|
||||
unsafe {
|
||||
pid = libc::fork();
|
||||
}
|
||||
let mut run_configs = runner::JudgeConfigs {
|
||||
exec_file: "/usr/bin/python3".to_string(),
|
||||
exec_args: vec![
|
||||
"/usr/bin/python3".to_string(),
|
||||
"-c".to_string(),
|
||||
"import requests; print(requests.get('https://httpbin.org/get').json())".to_string(),
|
||||
],
|
||||
test_cases: vec![],
|
||||
};
|
||||
if pid == 0 {
|
||||
unsafe {
|
||||
let exec_args = run_configs.exec_args().unwrap();
|
||||
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
|
||||
}
|
||||
} else if pid > 0 {
|
||||
println!("{:?}", pid);
|
||||
run_configs.test_cases.push(runner::TestCase {
|
||||
answer_file: "1.ans".to_string(),
|
||||
input_file: "1.in".to_string(),
|
||||
cpu_time_limit: 1000,
|
||||
real_time_limit: 1000,
|
||||
memory_limit: 65535,
|
||||
result: Some(runner::TestCaseResult::Accepted),
|
||||
});
|
||||
run_configs.test_cases.push(runner::TestCase {
|
||||
answer_file: "2.ans".to_string(),
|
||||
input_file: "2.in".to_string(),
|
||||
cpu_time_limit: 1000,
|
||||
real_time_limit: 1000,
|
||||
memory_limit: 65535,
|
||||
result: Some(runner::TestCaseResult::CompileError("compile error".to_string())),
|
||||
});
|
||||
run_configs.test_cases.push(runner::TestCase {
|
||||
answer_file: "3.ans".to_string(),
|
||||
input_file: "3.in".to_string(),
|
||||
cpu_time_limit: 1000,
|
||||
real_time_limit: 1000,
|
||||
memory_limit: 65535,
|
||||
result: Some(runner::TestCaseResult::WrongAnswer),
|
||||
});
|
||||
run_configs.test_cases.push(runner::TestCase {
|
||||
answer_file: "4.ans".to_string(),
|
||||
input_file: "4.in".to_string(),
|
||||
cpu_time_limit: 1000,
|
||||
real_time_limit: 1000,
|
||||
memory_limit: 65535,
|
||||
result: Some(runner::TestCaseResult::RuntimeError("runtime error".to_string())),
|
||||
});
|
||||
run_configs.test_cases.push(runner::TestCase {
|
||||
answer_file: "5.ans".to_string(),
|
||||
input_file: "5.in".to_string(),
|
||||
cpu_time_limit: 1000,
|
||||
real_time_limit: 1000,
|
||||
memory_limit: 65535,
|
||||
result: Some(runner::TestCaseResult::SystemError("system error".to_string())),
|
||||
});
|
||||
println!("{:?}", run_configs.test_cases[0].result);
|
||||
} else {
|
||||
println!("fork failure!");
|
||||
}
|
||||
process::run(&judge_config);
|
||||
}
|
||||
|
||||
110
src/process.rs
Normal file
110
src/process.rs
Normal file
@ -0,0 +1,110 @@
|
||||
extern crate libc;
|
||||
|
||||
use super::error::{Error, Result};
|
||||
use super::judger::{JudgeConfig, TestCase};
|
||||
use handlebars::Handlebars;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
pub struct ExecArgs {
|
||||
pub pathname: *const libc::c_char,
|
||||
pub argv: *const *const libc::c_char,
|
||||
pub envp: *const *const libc::c_char,
|
||||
}
|
||||
|
||||
impl ExecArgs {
|
||||
fn build<'b>(cmd: &'b String, judge_config: &'b JudgeConfig) -> Result<ExecArgs> {
|
||||
let mut handlebars = Handlebars::new();
|
||||
let _ = handlebars.register_template_string("build", cmd);
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
data.insert("filename", judge_config.code.file.clone());
|
||||
|
||||
let formatted = match handlebars.render("build", &data) {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Err(Error::TemplateRenderError(err)),
|
||||
};
|
||||
let splited = formatted.split_whitespace();
|
||||
let splited: Vec<&str> = splited.collect();
|
||||
|
||||
if splited.len() < 1 {
|
||||
return Err(Error::LanguageConfigError(formatted));
|
||||
}
|
||||
let pathname = splited[0].clone();
|
||||
let pathname_str = match CString::new(pathname) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||
};
|
||||
let pathname = pathname_str.as_ptr();
|
||||
|
||||
let mut argv_vec: Vec<*const libc::c_char> = vec![];
|
||||
for item in splited.iter() {
|
||||
let cstr = match CString::new(item.clone()) {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(Error::StringToCStringError(err)),
|
||||
};
|
||||
let cptr = cstr.as_ptr();
|
||||
// 需要使用 mem::forget 来标记
|
||||
// 否则在此次循环结束后,cstr 就会被回收,后续 exec 函数无法通过指针获取到字符串内容
|
||||
mem::forget(cstr);
|
||||
argv_vec.push(cptr);
|
||||
}
|
||||
// argv 与 envp 的参数需要使用 NULL 来标记结束
|
||||
argv_vec.push(ptr::null());
|
||||
let argv: *const *const libc::c_char = argv_vec.as_ptr() as *const *const libc::c_char;
|
||||
|
||||
// env 环境变量传 null,默认不传递任何环境变量,后续有需求可以修改此处
|
||||
let envp_vec: Vec<*const libc::c_char> = vec![ptr::null()];
|
||||
let envp = envp_vec.as_ptr() as *const *const libc::c_char;
|
||||
|
||||
mem::forget(pathname_str);
|
||||
mem::forget(argv_vec);
|
||||
mem::forget(envp_vec);
|
||||
|
||||
Ok(ExecArgs {
|
||||
pathname,
|
||||
argv,
|
||||
envp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ExecArgs {
|
||||
fn drop(&mut self) {
|
||||
// TODO: 将不安全的指针类型转换回内置类型,以便由 Rust 自动回收资源
|
||||
// 如果能保证资源能够正确 drop,不会出现泄漏,则 ExecArgs 相关的函数不必标注 unsafe
|
||||
println!("Dropping!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(judge_config: &JudgeConfig) {
|
||||
for test_case in &judge_config.tests {
|
||||
run_one(judge_config, test_case);
|
||||
println!("{}", test_case);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase) {
|
||||
println!("running test case: {}", test_case.index);
|
||||
|
||||
let pid;
|
||||
unsafe {
|
||||
pid = libc::fork();
|
||||
}
|
||||
|
||||
if pid == 0 { // 子进程
|
||||
// 此处如果出现 Error,则直接程序崩溃,父进程可以收集异常的信息
|
||||
let exec_args = ExecArgs::build(
|
||||
&judge_config.code.language.compile_command.clone(),
|
||||
&judge_config,
|
||||
).unwrap();
|
||||
unsafe {
|
||||
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
|
||||
}
|
||||
} else if pid > 0 { // 父进程
|
||||
println!("pid: {}", pid);
|
||||
} else { // 异常
|
||||
}
|
||||
}
|
||||
22
src/result.rs
Normal file
22
src/result.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResourceUsed {
|
||||
pub time_used: u32,
|
||||
pub memory_used: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TestCaseResult {
|
||||
Accepted(ResourceUsed),
|
||||
CompileError(ResourceUsed, String),
|
||||
WrongAnswer(ResourceUsed),
|
||||
RuntimeError(ResourceUsed, String),
|
||||
SystemError(ResourceUsed, String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TestCaseResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user