From 684e79057a3fa15d1416f7e7967585638d2a7698 Mon Sep 17 00:00:00 2001 From: MeiK Date: Sun, 26 Apr 2020 19:31:12 +0800 Subject: [PATCH] Update --- Cargo.toml | 1 + example/config.yml | 12 +++--- src/default.yml | 3 +- src/error.rs | 20 +++++++++- src/judger.rs | 6 ++- src/main.rs | 8 +++- src/process.rs | 93 +++++++++++++++++++++++++++++++++++++++------- 7 files changed, 118 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0bcc657..59cc60c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ edition = "2018" libc = "0.2" handlebars = "3.0.1" yaml-rust = "0.4" +tempfile = "3" diff --git a/example/config.yml b/example/config.yml index 8d200b7..c9b3505 100644 --- a/example/config.yml +++ b/example/config.yml @@ -16,12 +16,12 @@ judge_type: standard # standard / special # language: cpp extra_files: - hello.txt -code: - file: main.c - language: c +# code: +# file: main.c +# language: c # code: # file: main.cpp # language: cpp -# code: -# file: main.py -# language: python +code: + file: main.py + language: python diff --git a/src/default.yml b/src/default.yml index c5f59ea..4ec0412 100644 --- a/src/default.yml +++ b/src/default.yml @@ -14,4 +14,5 @@ languages: - language: python version: 3.6.9 compile_command: /usr/bin/python3 -m compileall {{filename}} - run_command: /use/bin/python3 {{filename}} + 1run_command: /usr/bin/python3 {{filename}} + run_command: /bin/ls diff --git a/src/error.rs b/src/error.rs index 2d984a1..53fe67a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,8 @@ use handlebars::RenderError; use libc::strerror; -use std::ffi::CStr; -use std::ffi::NulError; +use std::ffi::{CStr, NulError, OsString}; use std::fmt; +use std::io; use std::result; use yaml_rust::ScanError; @@ -19,6 +19,11 @@ pub enum Error { StringToCStringError(NulError), TemplateRenderError(RenderError), LanguageConfigError(String), + CreateTempDirError(io::Error), + CloseTempDirError(io::Error), + CopyFileError(io::Error), + OsStringToStringError(OsString), + ForkError(Option), } pub type Result = result::Result; @@ -47,6 +52,17 @@ impl fmt::Display for Error { write!(f, "UnknownJudgeType: {}", judge_type) } Error::PathJoinError => write!(f, "PathJoinError"), + Error::ForkError(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, "ForkError: {}", reason) + } _ => write!(f, "{:?}", self), } } diff --git a/src/judger.rs b/src/judger.rs index ca74812..b59d753 100644 --- a/src/judger.rs +++ b/src/judger.rs @@ -41,6 +41,7 @@ pub struct Code { } pub struct JudgeConfig { + pub config_dir: String, pub tests: Vec, pub judge_type: JudgeType, pub extra_files: Vec, @@ -48,7 +49,7 @@ pub struct JudgeConfig { } impl JudgeConfig { - fn load_yaml(global_config: &config::Config, yaml: &str) -> Result { + fn load_yaml(global_config: &config::Config, yaml: &str, path: &str) -> Result { let docs = match YamlLoader::load_from_str(yaml) { Ok(value) => value, Err(err) => return Err(Error::YamlScanError(err)), @@ -253,6 +254,7 @@ impl JudgeConfig { }; Ok(JudgeConfig { + config_dir: path.into(), tests, judge_type, extra_files, @@ -276,6 +278,6 @@ impl JudgeConfig { )) } }; - JudgeConfig::load_yaml(global_config, &config) + JudgeConfig::load_yaml(global_config, &config, &path) } } diff --git a/src/main.rs b/src/main.rs index cd790f7..5718374 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,5 +32,11 @@ fn main() { }; println!("{}", judge_config.code.language); - process::run(&judge_config); + match process::run(&judge_config) { + Ok(_) => {} + Err(err) => { + eprintln!("{}", err); + return; + } + }; } diff --git a/src/process.rs b/src/process.rs index 847a7aa..a52fe08 100644 --- a/src/process.rs +++ b/src/process.rs @@ -4,9 +4,15 @@ use super::error::{Error, Result}; use super::judger::{JudgeConfig, TestCase}; use handlebars::Handlebars; use std::collections::BTreeMap; +use std::env; use std::ffi::CString; +use std::fs; +use std::io; use std::mem; +use std::path::Path; use std::ptr; +use std::{thread, time}; +use tempfile::tempdir; pub struct ExecArgs { pub pathname: *const libc::c_char, @@ -55,8 +61,30 @@ impl ExecArgs { 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()]; + // env 环境变量传递当前进程环境变量 + let mut envp_vec: Vec<*const libc::c_char> = vec![]; + for (key, value) in env::vars_os() { + let mut key = match key.to_str() { + Some(val) => val.to_string(), + None => return Err(Error::OsStringToStringError(key)), + }; + let value = match value.to_str() { + Some(val) => val.to_string(), + None => return Err(Error::OsStringToStringError(value)), + }; + key.push_str("="); + key.push_str(&value); + let cstr = match CString::new(key) { + Ok(value) => value, + Err(err) => return Err(Error::StringToCStringError(err)), + }; + let cptr = cstr.as_ptr(); + // 需要使用 mem::forget 来标记 + // 否则在此次循环结束后,cstr 就会被回收,后续 exec 函数无法通过指针获取到字符串内容 + mem::forget(cstr); + envp_vec.push(cptr); + } + envp_vec.push(ptr::null()); let envp = envp_vec.as_ptr() as *const *const libc::c_char; mem::forget(pathname_str); @@ -79,32 +107,71 @@ impl Drop for ExecArgs { } } -pub fn run(judge_config: &JudgeConfig) { +pub fn run(judge_config: &JudgeConfig) -> Result<()> { + let pwd = match tempdir() { + Ok(val) => val, + Err(err) => return Err(Error::CreateTempDirError(err)), + }; + let from_dir = Path::new(&judge_config.config_dir); + let to_dir = pwd.path(); + + let mut copy_files: Vec = vec![]; + copy_files.push(judge_config.code.file.clone()); + copy_files.extend_from_slice(&judge_config.extra_files); for test_case in &judge_config.tests { - run_one(judge_config, test_case); - println!("{}", test_case); + copy_files.push(test_case.input_file.clone()); } + // copy file + for file in ©_files { + let from_file = from_dir.join(file); + let to_file = to_dir.join(file); + match fs::copy(from_file, to_file) { + Ok(_) => {} + Err(err) => return Err(Error::CopyFileError(err)), + }; + } + for test_case in &judge_config.tests { + let _ = run_one(judge_config, test_case, to_dir); + } + match pwd.close() { + Ok(_) => {} + Err(err) => return Err(Error::CloseTempDirError(err)), + }; + Ok(()) } -pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase) { +pub fn run_one(judge_config: &JudgeConfig, test_case: &TestCase, workdir: &Path) -> Result<()> { println!("running test case: {}", test_case.index); - let pid; + unsafe { pid = libc::fork(); } - - if pid == 0 { // 子进程 + if pid == 0 { + // 子进程 // 此处如果出现 Error,则直接程序崩溃,父进程可以收集异常的信息 + + // 修改工作目录 + env::set_current_dir(workdir).unwrap(); + let exec_args = ExecArgs::build( - &judge_config.code.language.compile_command.clone(), + &judge_config.code.language.run_command.clone(), &judge_config, - ).unwrap(); + ) + .unwrap(); unsafe { libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp); } - } else if pid > 0 { // 父进程 + // 理论上,不会走到这里,在上一句 exec 后,程序就已经被替换为待执行程序了 + // 所以, How dare you! + panic!("How dare you!"); + } else if pid > 0 { + // 父进程 println!("pid: {}", pid); - } else { // 异常 + println!("{:?}", workdir); + } else { + // 异常 + return Err(Error::ForkError(io::Error::last_os_error().raw_os_error())); } + Ok(()) }