mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
Update
This commit is contained in:
parent
af4bfeb837
commit
5e0223275c
1
docker/Dockerfile
Normal file
1
docker/Dockerfile
Normal file
@ -0,0 +1 @@
|
|||||||
|
FROM ubuntu:focal
|
||||||
3
src/config.rs
Normal file
3
src/config.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub static STDIN_FILENAME: &str = "stdin.txt";
|
||||||
|
pub static STDOUT_FILENAME: &str = "stdout.txt";
|
||||||
|
pub static STDERR_FILENAME: &str = "stderr.txt";
|
||||||
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
use libc::strerror;
|
use libc::strerror;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::ffi::NulError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::result;
|
use std::result;
|
||||||
use zip;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IOError(io::Error),
|
IOError(io::Error),
|
||||||
ZipError(zip::result::ZipError),
|
StringToCStringError(NulError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|||||||
52
src/exec_args.rs
Normal file
52
src/exec_args.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use super::error::{Error, Result};
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::println as debug;
|
||||||
|
|
||||||
|
pub struct ExecArgs {
|
||||||
|
pub pathname: *const libc::c_char,
|
||||||
|
pub argv: *const *const libc::c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecArgs {
|
||||||
|
pub fn build(cmd: &String) -> Result<ExecArgs> {
|
||||||
|
let cmds: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
|
let pathname = cmds[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 cmds.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;
|
||||||
|
|
||||||
|
mem::forget(pathname_str);
|
||||||
|
mem::forget(argv_vec);
|
||||||
|
Ok(ExecArgs { pathname, argv })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ExecArgs {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// TODO: 将不安全的指针类型转换回内置类型,以便由 Rust 自动回收资源
|
||||||
|
// TODO: 优先级较低,因为目前只在子进程里进行这个操作,且操作后会很快 exec,操作系统会回收这些内存
|
||||||
|
debug!("Dropping!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,10 @@ use std::pin::Pin;
|
|||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
|
|
||||||
|
mod config;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod exec_args;
|
||||||
|
mod process;
|
||||||
|
|
||||||
pub mod river {
|
pub mod river {
|
||||||
tonic::include_proto!("river");
|
tonic::include_proto!("river");
|
||||||
|
|||||||
182
src/process.rs
Normal file
182
src/process.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
use crate::exec_args::ExecArgs;
|
||||||
|
use libc;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::println as debug;
|
||||||
|
|
||||||
|
const STACK_SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Process {
|
||||||
|
pub cmd: String,
|
||||||
|
pub time_limit: i32,
|
||||||
|
pub memory_limit: i32,
|
||||||
|
pub workdir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct RunnerStatus {
|
||||||
|
pub rusage: libc::rusage,
|
||||||
|
pub exit_code: i32,
|
||||||
|
pub status: i32,
|
||||||
|
pub signal: i32,
|
||||||
|
pub time_used: i64,
|
||||||
|
pub memory_used: i64,
|
||||||
|
pub real_time_used: u128,
|
||||||
|
pub errmsg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
pub fn new(cmd: String, time_limit: i32, memory_limit: i32, workdir: PathBuf) -> Process {
|
||||||
|
let runner = Process {
|
||||||
|
cmd: cmd,
|
||||||
|
time_limit: time_limit,
|
||||||
|
memory_limit: memory_limit,
|
||||||
|
workdir: workdir,
|
||||||
|
};
|
||||||
|
runner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Runner {
|
||||||
|
process: Process,
|
||||||
|
pid: i32,
|
||||||
|
tx: Arc<Mutex<mpsc::Sender<RunnerStatus>>>,
|
||||||
|
rx: Arc<Mutex<mpsc::Receiver<RunnerStatus>>>,
|
||||||
|
stack: *mut libc::c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runner {
|
||||||
|
pub fn from(process: Process) -> Runner {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let stack = unsafe {
|
||||||
|
libc::mmap(
|
||||||
|
ptr::null_mut(),
|
||||||
|
STACK_SIZE,
|
||||||
|
libc::PROT_READ | libc::PROT_WRITE,
|
||||||
|
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_STACK,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Runner {
|
||||||
|
process: process,
|
||||||
|
pid: -1,
|
||||||
|
tx: Arc::new(Mutex::new(tx)),
|
||||||
|
rx: Arc::new(Mutex::new(rx)),
|
||||||
|
stack: stack,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Runner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug!("dropping");
|
||||||
|
unsafe {
|
||||||
|
libc::munmap(self.stack, STACK_SIZE);
|
||||||
|
|
||||||
|
let mut status = 0;
|
||||||
|
let pid = libc::waitpid(self.pid, &mut status, libc::WNOHANG);
|
||||||
|
|
||||||
|
// > 0: 对应子进程退出但未回收资源
|
||||||
|
// = 0: 对应子进程存在但未退出
|
||||||
|
// 如果在运行过程中上层异常中断,则需要 kill 子进程并回收资源
|
||||||
|
if pid >= 0 {
|
||||||
|
libc::kill(self.pid, 9);
|
||||||
|
libc::waitpid(self.pid, &mut status, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Runner {
|
||||||
|
type Output = i32;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<i32> {
|
||||||
|
let runner = Pin::into_inner(self);
|
||||||
|
// 如果 pid == -1,则说明子进程还没有运行
|
||||||
|
if runner.pid == -1 {
|
||||||
|
let waker = cx.waker().clone();
|
||||||
|
let pid = unsafe {
|
||||||
|
libc::clone(
|
||||||
|
runit,
|
||||||
|
(runner.stack as usize + STACK_SIZE) as *mut libc::c_void,
|
||||||
|
libc::SIGCHLD
|
||||||
|
| libc::CLONE_NEWUTS // 设置新的 UTS 名称空间(主机名、网络名等)
|
||||||
|
| libc::CLONE_NEWNET // 设置新的网络空间,如果没有配置网络,则该沙盒内部将无法联网
|
||||||
|
| libc::CLONE_NEWNS // 为沙盒内部设置新的 namespaces 空间
|
||||||
|
| libc::CLONE_NEWIPC // IPC 隔离
|
||||||
|
| libc::CLONE_NEWCGROUP // 在新的 CGROUP 中创建沙盒
|
||||||
|
| libc::CLONE_NEWPID, // 外部进程对沙盒不可见
|
||||||
|
&mut runner.process as *mut _ as *mut libc::c_void,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
debug!("pid = {}", pid);
|
||||||
|
runner.pid = pid;
|
||||||
|
|
||||||
|
// 因为 wait 会阻塞等待结果,因此此处使用 thread::spawn 来 wait,以防止主流程被阻塞
|
||||||
|
thread::spawn(move || {
|
||||||
|
Runner::waitpid(pid);
|
||||||
|
// 子流程运行结束后通知主流程
|
||||||
|
debug!("waker");
|
||||||
|
waker.wake();
|
||||||
|
});
|
||||||
|
return Poll::Pending;
|
||||||
|
} else {
|
||||||
|
return Poll::Ready(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runner {
|
||||||
|
fn waitpid(pid: i32) {
|
||||||
|
unsafe {
|
||||||
|
let mut status: i32 = 0;
|
||||||
|
// TODO: 获取运行状态等并传递给主流程
|
||||||
|
libc::wait4(pid, &mut status, 0, ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn runit(process: *mut libc::c_void) -> i32 {
|
||||||
|
let process = unsafe { &mut *(process as *mut Process) };
|
||||||
|
debug!("cmd = {}", process.cmd);
|
||||||
|
let exec_args = ExecArgs::build(&process.cmd).unwrap();
|
||||||
|
unsafe {
|
||||||
|
// TODO: 安全机制
|
||||||
|
// 设置主机名
|
||||||
|
libc::sethostname(CString::new("river").unwrap().as_ptr(), 5);
|
||||||
|
libc::setdomainname(CString::new("river").unwrap().as_ptr(), 5);
|
||||||
|
|
||||||
|
// 因为运行是在隔离的环境内,原有的环境变量已经没啥用了,因此这里直接传 null
|
||||||
|
libc::execve(exec_args.pathname, exec_args.argv, ptr::null_mut());
|
||||||
|
// 理论上并不会到这里,因此如果到这里,直接 kill 掉
|
||||||
|
libc::kill(libc::getpid(), libc::SIGKILL);
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::path::Path;
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test1() {
|
||||||
|
let process = Process::new(
|
||||||
|
String::from("/bin/echo Hello World!"),
|
||||||
|
1000,
|
||||||
|
65535,
|
||||||
|
Path::new("./").to_path_buf(),
|
||||||
|
);
|
||||||
|
let result = Runner::from(process).await;
|
||||||
|
assert_eq!(result, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user