diff --git a/src/cgroup.rs b/src/cgroup.rs deleted file mode 100644 index 30de676..0000000 --- a/src/cgroup.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::result; - -#[derive(Debug)] -pub enum Error { - Error(String), -} - -pub type Result = result::Result; - -pub struct Cgroup { - pub pid: u32, -} - -impl Cgroup { - pub fn new(pid: u32) -> Result { - return Ok(Cgroup { pid }); - } - pub fn attach(&self) -> Result<()> { - if self.pid == 0 { - return Err(Error::Error("Hello World!".to_string())); - } - return Ok(()); - } -} diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index d0718a5..0000000 --- a/src/config.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::fs; -use std::result; -use yaml_rust::{ScanError, Yaml, YamlLoader}; - -#[derive(Debug)] -pub enum Error { - YamlScanError(ScanError), - YamlParseError(String), - ReadFileError, - LanguageNotFound(String), -} - -pub type Result = result::Result; - -#[derive(Clone)] -pub struct LanguageConfig { - pub language: String, - pub version: String, - pub compile_command: String, - pub run_command: String, -} - -pub struct Config { - pub languages: Vec, -} - -impl Config { - pub fn language_config_from_name(&self, name: &str) -> Result { - for language in &self.languages { - if language.language == name { - return Ok(language.clone()) - } - } - Err(Error::LanguageNotFound(name.to_string())) - } - fn load_yaml(yaml: &str) -> Result { - let docs = match YamlLoader::load_from_str(yaml) { - Ok(value) => value, - Err(err) => return Err(Error::YamlScanError(err)), - }; - let doc = &docs[0]; - - // 读取语言配置 - let mut languages: Vec = vec![]; - let yaml_languages = match &doc["languages"] { - Yaml::Array(value) => value, - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 languages 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "解析错误,languages 字段的类型应该为 Array".to_string(), - )) - } - }; - for yaml_language in yaml_languages { - let language = match &yaml_language["language"] { - Yaml::String(value) => value.clone(), - _ => { - return Err(Error::YamlParseError( - "解析错误,language 字段的类型应该为 String".to_string(), - )) - } - }; - let version = match &yaml_language["version"] { - Yaml::String(value) => value.clone(), - _ => { - return Err(Error::YamlParseError( - "version 字段的类型应该为 String".to_string(), - )) - } - }; - let compile_command = match &yaml_language["compile_command"] { - Yaml::String(value) => value.clone(), - _ => { - return Err(Error::YamlParseError( - "compile_command 字段的类型应该为 String".to_string(), - )) - } - }; - let run_command = match &yaml_language["run_command"] { - Yaml::String(value) => value.clone(), - _ => { - return Err(Error::YamlParseError( - "run_command 字段的类型应该为 String".to_string(), - )) - } - }; - languages.push(LanguageConfig { - language, - version, - compile_command, - run_command, - }); - } - - Ok(Config { languages }) - } - pub fn load_from_file(filename: &str) -> Result { - let contents = match fs::read_to_string(filename) { - Ok(value) => value, - Err(_) => return Err(Error::ReadFileError), - }; - let config = Config::load_yaml(&contents)?; - Ok(config) - } - pub fn default() -> Result { - let config_yaml = include_str!("default.yml"); - let config = Config::load_yaml(config_yaml)?; - Ok(config) - } -} diff --git a/src/main.rs b/src/main.rs index 72beb6f..47ad8c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,109 +1,3 @@ -extern crate libc; - -use handlebars::Handlebars; -use std::collections::BTreeMap; -use std::env; - -mod config; -mod runner; - 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 = env::args().collect(); - let config = if args.len() > 1 { - config::Config::load_from_file(&args[1]) - } else { - config::Config::default() - }; - let config = match config { - Ok(value) => value, - Err(err) => { - eprintln!("{:?}", err); - return - } - }; - - let _ = match runner::JudgeConfigs::load(&config, "example") { - Ok(value) => value, - Err(err) => { - eprintln!("{:?}", err); - return - } - }; - - 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!"); - } + println!("Hello World!"); } diff --git a/src/runner.rs b/src/runner.rs deleted file mode 100644 index dc13d41..0000000 --- a/src/runner.rs +++ /dev/null @@ -1,383 +0,0 @@ -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 = result::Result; - -pub struct TestCase { - 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, -} - -#[derive(Debug)] -pub enum TestCaseResult { - Accepted, - CompileError(String), - WrongAnswer, - RuntimeError(String), - SystemError(String), -} - -pub struct JudgeCode { - pub file: String, - pub language: config::LanguageConfig, -} - -pub enum JudgeType { - Standard, - Special, -} - -pub struct TestConfig { - pub default_time_limit: u32, - pub default_real_time_limit: u32, - pub default_memory_limit: u32, - pub tests: Vec, - pub judge_type: JudgeType, - pub extra_files: Vec, - pub code: JudgeCode, -} - -pub struct JudgeConfigs { - pub exec_file: String, - pub exec_args: Vec, - pub test_cases: Vec, -} - -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 { - let docs = match YamlLoader::load_from_str(yaml) { - Ok(value) => value, - Err(err) => return Err(Error::YamlScanError(err)), - }; - let doc = &docs[0]; - - let default_time_limit = match &doc["time_limit"] { - Yaml::Integer(value) => value.clone() as u32, - Yaml::BadValue => 1000, - _ => { - return Err(Error::YamlParseError( - "解析错误,time_limit 字段的类型应该为 Integer".to_string(), - )) - } - }; - 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, - _ => { - return Err(Error::YamlParseError( - "解析错误,memory_limit 字段的类型应该为 Integer".to_string(), - )) - } - }; - let test_cases = match &doc["test_cases"] { - Yaml::Array(value) => value, - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 test_cases 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "test_cases 字段的类型应该为 Array".to_string(), - )) - } - }; - let mut tests: Vec = vec![]; - for case in test_cases { - let cpu_time_limit = match &case["time_limit"] { - Yaml::Integer(value) => value.clone() as u32, - Yaml::BadValue => default_time_limit, - _ => { - return Err(Error::YamlParseError( - "解析错误,time_limit 字段的类型应该为 Integer".to_string(), - )) - } - }; - let real_time_limit = match &case["real_time_limit"] { - Yaml::Integer(value) => value.clone() as u32, - Yaml::BadValue => cpu_time_limit * 2 + 5000, - _ => { - return Err(Error::YamlParseError( - "解析错误,real_time_limit 字段的类型应该为 Integer".to_string(), - )) - } - }; - let memory_limit = match &case["memory_limit"] { - Yaml::Integer(value) => value.clone() as u32, - Yaml::BadValue => default_memory_limit, - _ => { - return Err(Error::YamlParseError( - "解析错误,memory_limit 字段的类型应该为 Integer".to_string(), - )) - } - }; - let input_file = match &case["in"] { - Yaml::String(value) => value.clone(), - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 test_cases::in 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "test_cases::in 字段的类型应该为 String".to_string(), - )) - } - }; - let answer_file = match &case["ans"] { - Yaml::String(value) => value.clone(), - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 test_cases::ans 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "test_cases::ans 字段的类型应该为 String".to_string(), - )) - } - }; - tests.push(TestCase { - cpu_time_limit, - real_time_limit, - memory_limit, - result: None, - input_file, - answer_file, - }); - } - let judge_type = match &doc["judge_type"] { - Yaml::String(value) => { - if value == "standard" { - JudgeType::Standard - } else if value == "special" { - JudgeType::Special - } else { - return Err(Error::UnknownJudgeType(value.clone())); - } - } - Yaml::BadValue => JudgeType::Standard, - _ => { - return Err(Error::YamlParseError( - "judge_type 字段的类型应该为 String".to_string(), - )) - } - }; - let default_extra_files: Vec = vec![]; - let extra_files_yaml = match &doc["extra_files"] { - Yaml::Array(value) => value, - Yaml::BadValue => &default_extra_files, - _ => { - return Err(Error::YamlParseError( - "extra_files 字段的类型应该为 Array".to_string(), - )) - } - }; - let mut extra_files: Vec = vec![]; - for file in extra_files_yaml { - let name = match &file { - Yaml::String(value) => value.clone(), - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 extra_files 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "extra_files 字段的类型应该为 Array".to_string(), - )) - } - }; - extra_files.push(name); - } - let code = match &doc["code"] { - Yaml::Hash(value) => { - let file = match value.get(&Yaml::from_str("file")) { - Some(Yaml::String(val)) => val.clone(), - Some(Yaml::BadValue) => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 code::file 字段".to_string(), - )) - } - None => { - return Err(Error::YamlParseError( - "必须提供 code::file 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "code::file 字段的类型应该为 String".to_string(), - )) - } - }; - let language = match value.get(&Yaml::from_str("language")) { - Some(Yaml::String(val)) => match global_config.language_config_from_name(val) { - Ok(v) => v, - Err(_) => return Err(Error::LanguageNotFound(val.clone())), - }, - Some(Yaml::BadValue) => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 code::language 字段".to_string(), - )) - } - None => { - return Err(Error::YamlParseError( - "必须提供 code::language 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "code::language 字段的类型应该为 String".to_string(), - )) - } - }; - JudgeCode { file, language } - } - Yaml::BadValue => { - return Err(Error::YamlParseError( - "解析错误,无法解析到 code 字段".to_string(), - )) - } - _ => { - return Err(Error::YamlParseError( - "code 字段的类型应该为 Hash".to_string(), - )) - } - }; - - Ok(TestConfig { - default_time_limit, - default_real_time_limit, - default_memory_limit, - tests, - judge_type, - extra_files, - code, - }) - } - /** - * 读取评测文件夹 - */ - pub fn load(global_config: &config::Config, path: &str) -> Result { - let dir = Path::new(&path); - - let config = dir.join("config.yml"); - let config = match config.as_path().to_str() { - Some(value) => value, - None => return Err(Error::PathJoinError), - }; - 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 { - 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); - } - } - } -}