|
|
|
@ -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<P: AsRef<Path>>(container_work_path:P) -> Result<i32> {
|
|
|
|
|
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<P: AsRef<Path>>(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<bool> {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run_container(cmd: &String, wait:bool, log:bool, volume_path: &PathBuf) -> Result<String> {
|
|
|
|
|
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,10 +252,11 @@ 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();
|
|
|
|
|
|
|
|
|
@ -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()?
|
|
|
|
|
}
|
|
|
|
|