use crate::ApparmorMode;
use crate::ipc;
use crate::result::Result;
use crate::syscalls::capability;
use anyhow::{Context, bail};
use nix::unistd::{self, ForkResult, User};
use std::os::unix::io::AsRawFd;
use std::process;
mod child;
mod idmap_helper;
mod parent;
pub(crate) struct AttachOptions {
pub(crate) command: Option<String>,
pub(crate) arguments: Vec<String>,
pub(crate) container_name: String,
pub(crate) container_types: Vec<Box<dyn container_pid::Container>>,
pub(crate) effective_user: Option<User>,
pub(crate) apparmor_mode: ApparmorMode,
}
pub(crate) fn attach(opts: &AttachOptions) -> Result<std::convert::Infallible> {
if !capability::has_mount_api() {
bail!(
"Linux mount API is not available. cntr requires kernel 6.8+ with mount API support.\n\
Please upgrade your kernel or use an older version of cntr with FUSE support."
);
}
let process_status = crate::container::lookup_container(
&opts.container_name,
&opts.container_types,
opts.apparmor_mode,
)
.with_context(|| format!("failed to lookup container '{}'", opts.container_name))?;
let idmap_helper = if let Some(ref user) = opts.effective_user {
let current_uid = unistd::getuid(); let current_gid = unistd::getgid();
let target_uid = user.uid; let target_gid = user.gid;
let helper =
idmap_helper::IdmapHelper::new(target_uid, current_uid, target_gid, current_gid)
.context("failed to create idmap helper for --effective-user")?;
Some(helper)
} else {
None
};
let userns_fd = idmap_helper.as_ref().map(|h| h.userns_fd().as_raw_fd());
let effective_home = opts.effective_user.as_ref().map(|u| u.dir.clone());
let (parent_sock, child_sock) = ipc::socket_pair().context("failed to set up ipc")?;
let res = unsafe { unistd::fork() };
match res.context("failed to fork")? {
ForkResult::Parent { child } => {
drop(child_sock);
let result = parent::run(child, &process_status, &parent_sock);
drop(idmap_helper);
result
}
ForkResult::Child => {
drop(parent_sock);
let mut child_opts = child::ChildOptions {
command: opts.command.clone(),
arguments: opts.arguments.clone(),
process_status,
socket: &child_sock,
userns_fd,
effective_home,
};
let Err(e) = child::run(&mut child_opts);
eprintln!("attach child failed: {:?}", e);
process::exit(1);
}
}
}