|
|
|
@ -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<String>,
|
|
|
|
|
|
|
|
|
|
// --run /bin/bash --exec container_id
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
exec: Option<String>,
|
|
|
|
|
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
log: bool,
|
|
|
|
|
#[arg(long)]
|
|
|
|
@ -111,6 +115,7 @@ fn init_container_lock<P: AsRef<Path>>(container_work_path:P) -> Result<i32> {
|
|
|
|
|
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<P: AsRef<Path>>(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<bool> {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run_container(_container_id: &String, cmd: &String, args: &RockerArgs, volume_path: &PathBuf) -> Result<i32> {
|
|
|
|
|
// 禁止同时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<i32>{
|
|
|
|
|
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<i32> {
|
|
|
|
|
// 禁止同时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<ContainerInfo> {
|
|
|
|
|
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
|
|
|
|
|