use anyhow::{Context, bail};
use nix::unistd::{self, ForkResult};
use std::process;
use crate::ApparmorMode;
use crate::cmd::Cmd;
use crate::container_setup;
use crate::pty;
use crate::result::Result;
use crate::syscalls::capability;
pub(crate) struct ExecOptions {
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) apparmor_mode: ApparmorMode,
}
pub(crate) fn exec(opts: &ExecOptions) -> 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 mut 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 pty_master = pty::open_ptm().context("failed to open pty master")?;
let res = unsafe { unistd::fork() };
match res.context("failed to fork")? {
ForkResult::Parent { child } => {
pty::forward_pty_and_wait(&pty_master, child)
}
ForkResult::Child => {
let Err(e) = exec_child(
&mut process_status,
opts.command.clone(),
opts.arguments.clone(),
&pty_master,
);
eprintln!("exec child failed: {:?}", e);
process::exit(1);
}
}
}
fn exec_child(
process_status: &mut crate::procfs::ProcStatus,
exe: Option<String>,
args: Vec<String>,
pty_master: &nix::pty::PtyMaster,
) -> Result<std::convert::Infallible> {
pty::attach_pts(pty_master).context("failed to setup pty slave")?;
let exe = exe.or(Some(String::from("/bin/sh")));
let cmd = Cmd::new(exe.clone(), args, process_status.global_pid, None)
.with_context(|| format!("failed to prepare command {:?}", exe))?;
container_setup::enter_container(process_status).with_context(|| {
format!(
"failed to enter container with PID {}",
process_status.global_pid
)
})?;
let lsm_profile = process_status
.lsm_profile
.as_ref()
.map(|p| (p.own_path.clone(), p.label.clone()));
cmd.exec_in_container(lsm_profile)
.context("failed to execute command in container")
}