diff --git a/src/main.rs b/src/main.rs index b4df653..4997ef3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use std::arch::asm; use std::ffi::{CStr, CString}; use std::fs::File; -use std::os::fd::AsRawFd; +use std::os::fd::{AsFd, AsRawFd}; use std::os::unix::fs::PermissionsExt; use nix::libc::{self, setgid, CLONE_NEWCGROUP, MS_NODEV, MS_NOSUID}; -use nix::sched::{clone, CloneCb, CloneFlags}; +use nix::sched::{clone, CloneCb, CloneFlags, setns}; use nix::sys::signal::{kill, Signal}; use nix::sys::wait::{wait, waitpid, waitid, WaitPidFlag}; use nix::unistd::{chdir, chroot, dup2, execv, pivot_root, setuid, sleep, Gid, Pid, Uid, User, setgroups}; @@ -40,6 +40,10 @@ struct RockerArgs { #[arg(long)] env: Option, + // --run /bin/bash --exec container_id + #[arg(long)] + exec: Option, + #[arg(long)] log: bool, #[arg(long)] @@ -111,6 +115,7 @@ fn init_container_lock>(container_work_path:P) -> Result { let lock_path_str = lock_path.as_os_str(); let oflag = OFlag::O_RDWR | OFlag::O_CREAT; let mode = Mode::empty(); + println!("{lock_path_str:?}"); Ok(open(lock_path_str, oflag, mode)?) } @@ -130,11 +135,11 @@ fn init_container_overlay>(volume_path: P, upper_path: P, merged_ .arg(merged_dir) .output()?; - let std_out = String::from_utf8_lossy(&out.stdout); + // let std_out = String::from_utf8_lossy(&out.stdout); let std_err = String::from_utf8_lossy(&out.stderr); if std_err.len() == 0 { - println!("容器文件系统创建完成: {std_out:?}"); + println!("容器文件系统创建完成"); } else { return Err(RockerError::from(io::Error::new(io::ErrorKind::Other, format!("容器文件系统创建失败: {std_err:?}")))); } @@ -288,49 +293,20 @@ fn check_container_is_running(pid: &Pid, main_exe: &Path) -> Result { Ok(false) } -fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume_path: &PathBuf) -> Result { - // 禁止同时wait和log - if args.wait && args.log { - return Err(RockerError::OtherError("--wait/--log 禁止同时使用".to_string())); - } - - // 初始化容器工作目录 - let container_work_path = Path::new(WORKSPACE).join("containers").join(&_container_id); - let container_upper_path = container_work_path.join("upper"); - let container_merged_path = container_work_path.join("merged"); - create_dir(&container_work_path, true)?; - create_dir(&container_upper_path, true)?; - create_dir(&container_merged_path, true)?; +fn init_exec_ns(pid: i32) -> Result<()>{ + // 把当前进程加入到指定pid的namespace + for ns_name in vec!["ipc", "uts", "net", "pid", "mnt"] { + let ns_path = format!("/proc/{pid}/ns/{ns_name}"); + let ns_fild = File::open(ns_path)?; + setns(ns_fild.as_fd(), CloneFlags::from_bits_retain(0))? } + Ok(()) +} - let rocker_user_info = User::from_name(USER_NAME)?.ok_or(RockerError::OtherError(format!("没找到 用户: {USER_NAME}")))?; - let rocker_uid = rocker_user_info.uid; - let rocker_gid = rocker_user_info.gid; - let _cb = || { - init_container_lock(&container_work_path).unwrap(); - init_container_overlay(volume_path, &container_upper_path, &container_merged_path).unwrap(); - if let Some(custom_volume) = &args.volume { - init_container_custom_volume(&container_merged_path, custom_volume).unwrap(); - } - init_container_pivot(&container_merged_path).unwrap(); - init_container_mount().unwrap(); - init_container_log(args.log).unwrap(); - init_container_env().unwrap(); - init_container_user(rocker_uid, rocker_gid).unwrap(); - - let cmd_vec = parse_cmd(cmd); - match execv(&cmd_vec[0], &cmd_vec) { - Err(e) => { - println!("execv {cmd_vec:?}失败: {e:?}"); - } - _ => {}, - }; - 0 - }; - +fn start(is_wait: bool, cb: CloneCb, clong_flags: CloneFlags) -> Result{ let main_exe = std::env::current_exe()?; unsafe { - match clone(Box::new(_cb), STACK.as_mut_slice(), CloneFlags::from_bits_truncate(CLONE_FLAG), None) { + match clone(cb, STACK.as_mut_slice(), clong_flags, None) { Ok(child_pid) => { println!("clone ok: {child_pid:?}"); @@ -349,7 +325,7 @@ fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume } // wait - if args.wait { + if is_wait { match waitpid(child_pid, Some(WaitPidFlag::WUNTRACED)) { Ok(status) => { println!("{child_pid:?} exit: {status:?}"); @@ -362,12 +338,81 @@ fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume Ok(child_pid.as_raw()) } Err(e) => { - return Err(RockerError::OtherError(format!("clone err: {e}"))); + Err(RockerError::OtherError(format!("clone err: {e}"))) } } } } +fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume_path: &PathBuf, is_exec: bool) -> Result { + // 禁止同时wait和log + if args.wait && args.log { + return Err(RockerError::OtherError("--wait/--log 禁止同时使用".to_string())); + } + + let clone_flags; + + // 初始化容器工作目录 + let container_work_path = Path::new(WORKSPACE).join("containers").join(&_container_id); + let container_upper_path = container_work_path.join("upper"); + let container_merged_path = container_work_path.join("merged"); + create_dir(&container_work_path, true)?; + create_dir(&container_upper_path, true)?; + create_dir(&container_merged_path, true)?; + + let rocker_user_info = User::from_name(USER_NAME)?.ok_or(RockerError::OtherError(format!("没找到 用户: {USER_NAME}")))?; + let rocker_uid = rocker_user_info.uid; + let rocker_gid = rocker_user_info.gid; + + let _cb = if is_exec { + let _cb = move || { + let container_info = get_container_info(_container_id).unwrap(); + init_exec_ns(container_info.pid).unwrap(); + init_container_env().unwrap(); + init_container_user(rocker_uid, rocker_gid).unwrap(); + + let cmd_vec = parse_cmd(cmd); + match execv(&cmd_vec[0], &cmd_vec) { + Err(e) => { + println!("execv {cmd_vec:?}失败: {e:?}"); + } + _ => {}, + }; + 0isize + }; + + clone_flags = CloneFlags::empty(); + Box::new(_cb) as CloneCb + } else { + let _cb = move || { + init_container_lock(&container_work_path).unwrap(); + init_container_overlay(volume_path, &container_upper_path, &container_merged_path).unwrap(); + if let Some(custom_volume) = &args.volume { + init_container_custom_volume(&container_merged_path, custom_volume).unwrap(); + } + init_container_pivot(&container_merged_path).unwrap(); + init_container_mount().unwrap(); + init_container_log(args.log).unwrap(); + init_container_env().unwrap(); + init_container_user(rocker_uid, rocker_gid).unwrap(); + + let cmd_vec = parse_cmd(cmd); + match execv(&cmd_vec[0], &cmd_vec) { + Err(e) => { + println!("execv {cmd_vec:?}失败: {e:?}"); + } + _ => {}, + }; + 0isize + }; + + clone_flags = CloneFlags::from_bits_truncate(CLONE_FLAG); + Box::new(_cb) as CloneCb + }; + + start(args.wait, _cb, clone_flags) +} + #[derive(Deserialize, Serialize, Debug, PartialEq)] enum ContainerStatus { READY, @@ -428,8 +473,8 @@ fn get_container_info(container_id: &str) -> Result { let lock_path = container_work_path.join(LOCK_FILE); let info_str = fs::read_to_string(container_info_path)?; let mut container_info: ContainerInfo = toml::from_str(&info_str)?; - - // 判断是否正在运行, 首先得到该容器所有的fd + + // 判断是否正在运行, 首先得到该容器进程对应的所有的fd let proc_fd_path = Path::new("/proc").join(container_info.pid.to_string()).join("fd"); let is_running = if let Ok(fd_dir) = fs::read_dir(proc_fd_path) { fd_dir.filter_map(|p|p.ok()) @@ -475,25 +520,36 @@ fn stop_container(containers_id: &str, is_remove: bool) -> Result<()> { if let Ok(container_info) = get_container_info(container_id) { let container_work_path = Path::new(WORKSPACE).join("containers").join(container_id); let container_merged_path = container_work_path.join("merged"); + println!("container_merged_path: {container_merged_path:?}"); // 正在运行中的需要 kill if container_info.status == ContainerStatus::RUNNING { let _ = kill(Pid::from_raw(container_info.pid), Signal::SIGTERM); + let pid_path = Path::new("/proc").join(container_info.pid.to_string()); + while pid_path.exists() { + std::thread::sleep(std::time::Duration::from_millis(10)); + } } // 卸载自定义挂载点 if container_info.volume != "" { container_info.volume .split(",") .filter_map(|v| v.split(":").last()) - .map(|v| container_merged_path.join(v).to_string_lossy().to_string()) + .map(|v| { + if v.starts_with("/") { + container_merged_path.join(&v[1..]).to_string_lossy().to_string() + } else { + container_merged_path.join(v).to_string_lossy().to_string() + } + }) .for_each(|s| { - match umount(s.as_str()) { - Ok(_) => println!("卸载自定卷"), - Err(e) => panic!("卸载卷失败: {e:?}"), + match umount2(s.as_str(), MntFlags::MNT_DETACH) { + Ok(_) => println!("卸载自定卷{s}"), + Err(e) => println!("卸载卷{s}失败: {e:?}"), } }); } // 卸载overlayfs - match umount(container_merged_path.to_str().unwrap()) { + match umount2(container_merged_path.to_str().unwrap(), MntFlags::MNT_DETACH) { Ok(_) => println!("卸载overlayfs卷"), Err(e) => println!("卸载overlayfs失败: {e:?}"), } @@ -522,7 +578,7 @@ fn main() -> Result<()>{ let volume_path = extend_image(image_name)?; let container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string(); let mut pid = -1; - match run_container(&container_id,&cmd, &args, &volume_path) { + match run_container(&container_id,&cmd, &args, &volume_path, false) { Ok(child_pid) => { pid = child_pid; } @@ -540,6 +596,8 @@ fn main() -> Result<()>{ } else if let Some(containers_id) = &args.stop { // --stop stop_container(containers_id, false)?; + } else if let (Some(cmd), Some(container_id)) = (&args.run, &args.exec) { + run_container(container_id, &cmd, &args, &Default::default(), true).unwrap(); } // } else if let Some(containers_id) = &args.start { // // --start