From ea98ae18314c2811dbd98128b0807d8cbccebf95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=B3=E5=85=89=E5=B0=91=E5=B9=B4?= <849317537@qq.com> Date: Wed, 31 Jul 2024 03:21:14 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=80=E4=B8=AAlock?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/main.rs | 79 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 27 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f97022 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9408542..1ae5816 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::ffi::{CStr, CString}; use std::fs::File; use std::os::fd::AsRawFd; use std::os::unix::fs::PermissionsExt; -use nix::libc::{setgid, CLONE_NEWCGROUP, MS_NODEV, MS_NOSUID}; +use nix::libc::{self, setgid, CLONE_NEWCGROUP, MS_NODEV, MS_NOSUID}; use nix::sched::{clone, CloneCb, CloneFlags}; use nix::sys::wait::{wait, waitpid, waitid, WaitPidFlag}; use nix::unistd::{chdir, chroot, dup2, execv, pivot_root, setuid, sleep, Gid, Pid, Uid, User, setgroups}; @@ -17,9 +17,12 @@ use uuid; use std::{io, fs}; use toml; use serde::{Deserialize, Serialize}; +use std::fmt::Display; static WORKSPACE: &str = "/root/rocker"; static USER_NAME: &str = "rocker"; +static INFO_FILE: &str = "info.toml"; +static LOCK_FILE: &str = ".lock"; static mut STACK: [u8; 1024*1024*1] = [0; 1024*1024*1]; static CLONE_FLAG: i32 = 0b1101100000000100000000000000000; // CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNET; @@ -49,24 +52,22 @@ struct RockerArgs { /// 从images解压到volumes fn extend_image(image_name: &String) -> Result<(PathBuf)> { + // 源文件 + let image_path = Path::new(WORKSPACE).join("images").join(image_name); + if image_path.exists() == false { + return Err(RockerError::from(io::Error::new(io::ErrorKind::NotFound, "未找到镜像"))); + } + let image_path_str = image_path.to_str().unwrap(); // 安全的unwrap + // volumes只读层 let volume_path = Path::new(WORKSPACE).join("volumes").join(image_name); if volume_path.exists() { return Ok(volume_path); } else { - // std::fs::create_dir_all(&volume_path)?; - // std::fs::set_permissions(&volume_path, PermissionsExt::from_mode(0o777))?; create_dir_and_set777(&volume_path)?; } let volume_path_str = volume_path.to_str().unwrap(); // 安全的unwrap - - // 源文件 - let image_path = Path::new(WORKSPACE).join("images").join(image_name); - if image_path.exists() == false { - return Err(RockerError::from(io::Error::new(io::ErrorKind::NotFound, "未找到镜像"))); - } - let image_path_str = image_path.to_str().unwrap(); // 安全的unwrap - + // 解压缩 let out = std::process::Command::new("tar") .arg("-xvf") @@ -78,12 +79,23 @@ fn extend_image(image_name: &String) -> Result<(PathBuf)> { let std_err = String::from_utf8_lossy(&out.stderr); if std_err.len() == 0 { println!("解压缩完毕: {std_out:?}"); + Ok(volume_path) } else { - return Err(RockerError::from(io::Error::new(io::ErrorKind::Other, format!("解压缩镜像失败: {std_err}")))); + // 删除 volume_path + std::fs::remove_dir_all(volume_path)?; + Err(RockerError::from(io::Error::new(io::ErrorKind::Other, format!("解压缩镜像失败: {std_err}")))) } - Ok(volume_path) } +fn init_container_lock>(container_work_path:P) -> Result { + use nix::sys::stat::Mode; + use nix::fcntl::{OFlag, open}; + let lock_path = container_work_path.as_ref().join(LOCK_FILE); + let lock_path_str = lock_path.as_os_str(); + let oflag = OFlag::O_RDWR | OFlag::O_CREAT; + let mode = Mode::empty(); + Ok(open(lock_path_str, oflag, mode)?) +} fn init_container_overlay>(volume_path: P, upper_path: P, merged_path: P) -> Result<()> { let lower_dir = volume_path.as_ref().to_string_lossy().to_string(); @@ -220,9 +232,9 @@ fn check_container_is_running(pid: &Pid, main_exe: &Path) -> Result { Ok(false) } -fn run_container(cmd: &String, wait:bool, log:bool, volume_path: &PathBuf) -> Result { +fn run_container(cmd: &String, args: &RockerArgs, volume_path: &PathBuf) -> Result<(String, i32)> { // 禁止同时wait和log - if wait && log { + if args.wait && args.log { return Err(RockerError::OtherError("--wait/--log 禁止同时使用".to_string())); } @@ -240,13 +252,14 @@ fn run_container(cmd: &String, wait:bool, log:bool, volume_path: &PathBuf) -> Re 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(); init_container_pivot(&container_merged_path).unwrap(); init_container_mount().unwrap(); - init_container_log(log).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); execv(&cmd_vec[0], &cmd_vec).unwrap(); 0 @@ -266,7 +279,7 @@ fn run_container(cmd: &String, wait:bool, log:bool, volume_path: &PathBuf) -> Re } // wait - if wait { + if args.wait { match waitpid(child_pid, Some(WaitPidFlag::WUNTRACED)) { Ok(status) => { println!("{child_pid:?} exit: {status:?}"); @@ -276,32 +289,44 @@ fn run_container(cmd: &String, wait:bool, log:bool, volume_path: &PathBuf) -> Re } } } + Ok((_container_id, child_pid.as_raw())) } Err(e) => { return Err(RockerError::OtherError(format!("clone err: {e}"))); } } } - Ok(_container_id) } #[derive(Deserialize, Serialize, Debug)] struct ContainerInfo { id: String, + pid: i32, run: String, image: String, volume: String, env: String, + status: String, +} + +impl Display for ContainerInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let volume: String = self.volume.chars().take(20).collect(); + let env: String = self.env.chars().take(20).collect(); + write!(f, "{:<10}{:<8}{:<10}{:<20}{:<20}{:<20}{:<10}", self.id, self.pid, self.image, self.run, volume, env, &self.status) + } } -fn save_container_info(args: &RockerArgs, container_id: &String) -> Result<()> { - let container_info_path = Path::new(WORKSPACE).join("containers").join(container_id).join("info.toml"); +fn save_container_info(args: &RockerArgs, container_id: &String, pid: i32) -> Result<()> { + let container_info_path = Path::new(WORKSPACE).join("containers").join(container_id).join(INFO_FILE); let container_info = ContainerInfo { id: container_id.clone(), + pid: pid, run: args.run.as_ref().unwrap().clone(), image: args.image.as_ref().unwrap().clone(), volume: "".to_string(), - env: "".to_string() + env: "".to_string(), + status: "".to_string(), }; let toml_str = toml::to_string(&container_info)?; fs::write(container_info_path, toml_str)?; @@ -312,13 +337,13 @@ fn save_container_info(args: &RockerArgs, container_id: &String) -> Result<()> { fn show_containers() -> Result<()> { let containers_path = Path::new(WORKSPACE).join("containers"); - println!("{:<20}{:<20}{:<20}{:<20}{:<20}", "id", "image", "run", "volume", "env"); + println!("{:<10}{:<8}{:<10}{:<20}{:<20}{:<20}{:<10}", "id", "pid", "image", "run", "volume", "env", "status"); for entry in fs::read_dir(containers_path)? { let path = entry?.path(); - let info_path = path.join("info.toml"); + let info_path = path.join(INFO_FILE); let info_str = fs::read_to_string(info_path)?; let container_info: ContainerInfo = toml::from_str(&info_str)?; - println!("{:<20}{:<20}{:<20}{:<20}{:<20}", container_info.id, container_info.image, container_info.run, &container_info.volume[0..20], &container_info.env[0..20]); + println!("{}", container_info); } Ok(()) } @@ -329,8 +354,8 @@ fn main() -> Result<()>{ // run if let (Some(cmd), Some(image_name)) = (&args.run, &args.image) { let volume_path = extend_image(image_name)?; - let container_id = run_container(cmd, args.wait, args.log, &volume_path)?; - save_container_info(&args, &container_id)?; + let (container_id, pid) = run_container(cmd, &args, &volume_path)?; + save_container_info(&args, &container_id, pid)?; } else if args.ps { show_containers()? }