use super::BootLog;
use super::Dns;
use super::ExitStatus;
use super::GIB;
use super::Hosts;
use super::LinuxProcess;
use super::LinuxProcessConfiguration;
use super::Mount;
use super::NatInterface;
use super::Signal;
use super::UnixSocketConfiguration;
use super::strings;
use crate::containerization_os::terminal;
use crate::error::Error;
use crate::platform;
use crate::platform::ffi;
use std::collections::BTreeMap;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum SeccompProfile {
#[default]
Unconfined,
Default,
Profile(String),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Configuration {
pub process: LinuxProcessConfiguration,
pub cpus: u32,
pub memory_in_bytes: u64,
pub hostname: Option<String>,
pub sysctl: BTreeMap<String, String>,
pub interfaces: Vec<NatInterface>,
pub sockets: Vec<UnixSocketConfiguration>,
pub mounts: Vec<Mount>,
pub masked_paths: Vec<String>,
pub readonly_paths: Vec<String>,
pub dns: Option<Dns>,
pub hosts: Option<Hosts>,
pub virtualization: bool,
pub boot_log: Option<BootLog>,
pub oci_runtime_path: Option<String>,
pub seccomp_profile: SeccompProfile,
pub use_init: bool,
}
impl Default for Configuration {
fn default() -> Self {
Self {
process: LinuxProcessConfiguration::default(),
cpus: 4,
memory_in_bytes: GIB,
hostname: None,
sysctl: BTreeMap::new(),
interfaces: Vec::new(),
sockets: Vec::new(),
mounts: LinuxContainer::default_mounts(),
masked_paths: LinuxContainer::default_masked_paths(),
readonly_paths: LinuxContainer::default_readonly_paths(),
dns: None,
hosts: None,
virtualization: false,
boot_log: None,
oci_runtime_path: None,
seccomp_profile: SeccompProfile::Unconfined,
use_init: false,
}
}
}
pub struct LinuxContainer {
pub(crate) handle: ffi::CzLinuxContainer,
}
unsafe impl Send for LinuxContainer {}
unsafe impl Sync for LinuxContainer {}
impl LinuxContainer {
pub const MAX_ID_LENGTH: usize = 64;
pub fn id(&self) -> String {
self.handle.id()
}
pub fn create(&self) -> Result<(), Error> {
platform::outcome(self.handle.create(), format!("create {}", self.id())).map(|_| ())
}
pub fn start(&self) -> Result<(), Error> {
platform::outcome(self.handle.start(), format!("start {}", self.id())).map(|_| ())
}
pub fn stop(&self) -> Result<(), Error> {
platform::outcome(self.handle.stop(), format!("stop {}", self.id())).map(|_| ())
}
pub fn kill(&self, signal: Signal) -> Result<(), Error> {
platform::outcome(self.handle.kill(signal.raw_value), format!("signal {}", self.id())).map(|_| ())
}
pub fn wait(&self, timeout_in_seconds: Option<i64>) -> Result<ExitStatus, Error> {
platform::outcome(self.handle.wait(timeout_in_seconds), format!("wait for {}", self.id()))
.map(|outcome| platform::exit_status(&outcome))
}
pub fn resize(&self, to: terminal::Size) -> Result<(), Error> {
platform::outcome(self.handle.resize(to.width, to.height), format!("resize {}", self.id())).map(|_| ())
}
pub fn exec(&self, id: &str, configuration: LinuxProcessConfiguration) -> Result<LinuxProcess, Error> {
platform::outcome(
self.handle.exec(id, configuration),
format!("exec {id} in {}", self.id()),
)
.map(|outcome| LinuxProcess {
handle: outcome.linux_process(),
})
}
pub fn close_stdin(&self) -> Result<(), Error> {
platform::outcome(self.handle.close_stdin(), format!("close {}'s stdin", self.id())).map(|_| ())
}
pub fn default_mounts() -> Vec<Mount> {
let defaults = ["nosuid", "noexec", "nodev"];
vec![
Mount::any("proc", "proc", "/proc", &[]),
Mount::any("sysfs", "sysfs", "/sys", &defaults),
Mount::any("devtmpfs", "none", "/dev", &["nosuid", "mode=755"]),
Mount::any("mqueue", "mqueue", "/dev/mqueue", &defaults),
Mount::any(
"tmpfs",
"tmpfs",
"/dev/shm",
&["nosuid", "noexec", "nodev", "mode=1777", "size=65536k"],
),
Mount::any("cgroup2", "none", "/sys/fs/cgroup", &defaults),
Mount::any(
"devpts",
"devpts",
"/dev/pts",
&["nosuid", "noexec", "newinstance", "gid=5", "mode=0620", "ptmxmode=0666"],
),
]
}
pub fn default_masked_paths() -> Vec<String> {
strings(&[
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
"/sys/devices/virtual/powercap",
])
}
pub fn default_readonly_paths() -> Vec<String> {
strings(&["/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger"])
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_a_configuration_as_containerization_does() {
let configuration = Configuration::default();
assert_eq!(configuration.cpus, 4);
assert_eq!(configuration.memory_in_bytes, GIB);
assert_eq!(configuration.mounts.len(), 7);
assert!(
configuration
.masked_paths
.contains(&"/proc/kcore".to_string())
);
assert!(
configuration
.readonly_paths
.contains(&"/proc/sys".to_string())
);
assert_eq!(configuration.dns, None);
assert_eq!(configuration.seccomp_profile, SeccompProfile::Unconfined);
assert_eq!(configuration.process.working_directory, "/");
assert_eq!(
configuration.process.environment_variables,
[format!("PATH={}", LinuxProcessConfiguration::DEFAULT_PATH)]
);
}
}