diff --git a/Cargo.lock b/Cargo.lock index 376e3bac..930ef892 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,6 +171,37 @@ dependencies = [ "byteorder", ] +[[package]] +name = "container" +version = "0.0.1" +dependencies = [ + "anyhow", + "caps", + "cgroups", + "chrono", + "crossbeam-channel", + "dbus", + "fastrand", + "futures", + "libc", + "log", + "mio", + "nix", + "oci-spec 0.5.2 (git+https://github.com/containers/oci-spec-rs?rev=d6fb1e91742313cd0d0085937e2d6df5d4669720)", + "once_cell", + "path-clean", + "pentacle", + "prctl", + "procfs", + "quickcheck", + "seccomp", + "serde", + "serde_json", + "serial_test", + "systemd", + "tabwriter", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -1388,29 +1419,18 @@ name = "youki" version = "0.0.1" dependencies = [ "anyhow", - "caps", "cgroups", "chrono", "clap", - "crossbeam-channel", - "dbus", - "fastrand", - "futures", - "libc", + "container", "log", - "mio", "nix", "oci-spec 0.5.2 (git+https://github.com/containers/oci-spec-rs?rev=d6fb1e91742313cd0d0085937e2d6df5d4669720)", "once_cell", - "path-clean", "pentacle", - "prctl", "procfs", - "quickcheck", - "seccomp", "serde", "serde_json", "serial_test", - "systemd", "tabwriter", ] diff --git a/Cargo.toml b/Cargo.toml index ce04afdd..00c6f7d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,54 +1,8 @@ -[package] -name = "youki" -version = "0.0.1" -authors = ["utam0k "] -edition = "2018" -description = "A container runtime written in Rust" - [workspace] members = [ "crates/*" ] -[features] -default = ["systemd_cgroups"] -systemd_cgroups = ["systemd"] - -[dependencies.clap] -version = "3.0.0-beta.2" -default-features = false -features = ["std", "suggestions", "derive", "cargo"] - -[dependencies] -nix = "0.22.0" -procfs = "0.11.0" -# Waiting for new caps release, replace git with version on release -caps = { git = "https://github.com/lucab/caps-rs", rev = "cb54844", features = ["serde_support"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -prctl = "1.0.0" -libc = "0.2.84" -log = "0.4" -anyhow = "1.0" -mio = { version = "0.7.13", features = ["os-ext", "os-poll"] } -chrono = { version="0.4", features = ["serde"] } -once_cell = "1.6.0" -futures = { version = "0.3", features = ["thread-pool"] } -oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "d6fb1e91742313cd0d0085937e2d6df5d4669720" } -cgroups = { version = "0.1.0", path = "./crates/cgroups" } -systemd = { version = "0.8", default-features = false, optional = true } -dbus = "0.9.2" -tabwriter = "1" -fastrand = "1.4.1" -crossbeam-channel = "0.5" -seccomp = { version = "0.1.0", path = "./crates/seccomp" } -pentacle = "1.0.0" -path-clean = "0.1.0" - -[dev-dependencies] -oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "d6fb1e91742313cd0d0085937e2d6df5d4669720", features = ["proptests"] } -quickcheck = "1" -serial_test = "0.5.1" [profile.release] lto = true diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml new file mode 100644 index 00000000..b109c201 --- /dev/null +++ b/crates/container/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "container" +version = "0.0.1" +authors = ["youki team"] +edition = "2018" +description = "A container runtime written in Rust" + +[features] +default = ["systemd_cgroups"] +systemd_cgroups = ["systemd"] + + +[dependencies] +nix = "0.22.0" +procfs = "0.11.0" +# Waiting for new caps release, replace git with version on release +caps = { git = "https://github.com/lucab/caps-rs", rev = "cb54844", features = ["serde_support"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +prctl = "1.0.0" +libc = "0.2.84" +log = "0.4" +anyhow = "1.0" +mio = { version = "0.7.13", features = ["os-ext", "os-poll"] } +chrono = { version="0.4", features = ["serde"] } +once_cell = "1.6.0" +futures = { version = "0.3", features = ["thread-pool"] } +oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "d6fb1e91742313cd0d0085937e2d6df5d4669720" } +cgroups = { version = "0.1.0", path = "../cgroups" } +systemd = { version = "0.8", default-features = false, optional = true } +dbus = "0.9.2" +tabwriter = "1" +fastrand = "1.4.1" +crossbeam-channel = "0.5" +seccomp = { version = "0.1.0", path = "../seccomp" } +pentacle = "1.0.0" +path-clean = "0.1.0" + +[dev-dependencies] +oci-spec = { git = "https://github.com/containers/oci-spec-rs", rev = "d6fb1e91742313cd0d0085937e2d6df5d4669720", features = ["proptests"] } +quickcheck = "1" +serial_test = "0.5.1" diff --git a/src/apparmor.rs b/crates/container/src/apparmor.rs similarity index 100% rename from src/apparmor.rs rename to crates/container/src/apparmor.rs diff --git a/src/capabilities.rs b/crates/container/src/capabilities.rs similarity index 100% rename from src/capabilities.rs rename to crates/container/src/capabilities.rs diff --git a/src/container/builder.rs b/crates/container/src/container/builder.rs similarity index 100% rename from src/container/builder.rs rename to crates/container/src/container/builder.rs diff --git a/src/container/builder_impl.rs b/crates/container/src/container/builder_impl.rs similarity index 100% rename from src/container/builder_impl.rs rename to crates/container/src/container/builder_impl.rs diff --git a/src/container/container.rs b/crates/container/src/container/container.rs similarity index 100% rename from src/container/container.rs rename to crates/container/src/container/container.rs diff --git a/src/container/container_delete.rs b/crates/container/src/container/container_delete.rs similarity index 100% rename from src/container/container_delete.rs rename to crates/container/src/container/container_delete.rs diff --git a/src/container/container_events.rs b/crates/container/src/container/container_events.rs similarity index 100% rename from src/container/container_events.rs rename to crates/container/src/container/container_events.rs diff --git a/src/container/container_kill.rs b/crates/container/src/container/container_kill.rs similarity index 100% rename from src/container/container_kill.rs rename to crates/container/src/container/container_kill.rs diff --git a/src/container/container_pause.rs b/crates/container/src/container/container_pause.rs similarity index 100% rename from src/container/container_pause.rs rename to crates/container/src/container/container_pause.rs diff --git a/src/container/container_resume.rs b/crates/container/src/container/container_resume.rs similarity index 100% rename from src/container/container_resume.rs rename to crates/container/src/container/container_resume.rs diff --git a/src/container/container_start.rs b/crates/container/src/container/container_start.rs similarity index 100% rename from src/container/container_start.rs rename to crates/container/src/container/container_start.rs diff --git a/src/container/init_builder.rs b/crates/container/src/container/init_builder.rs similarity index 100% rename from src/container/init_builder.rs rename to crates/container/src/container/init_builder.rs diff --git a/src/container/mod.rs b/crates/container/src/container/mod.rs similarity index 100% rename from src/container/mod.rs rename to crates/container/src/container/mod.rs diff --git a/src/container/state.rs b/crates/container/src/container/state.rs similarity index 100% rename from src/container/state.rs rename to crates/container/src/container/state.rs diff --git a/src/container/tenant_builder.rs b/crates/container/src/container/tenant_builder.rs similarity index 100% rename from src/container/tenant_builder.rs rename to crates/container/src/container/tenant_builder.rs diff --git a/src/dbus/client.rs b/crates/container/src/dbus/client.rs similarity index 100% rename from src/dbus/client.rs rename to crates/container/src/dbus/client.rs diff --git a/src/dbus/mod.rs b/crates/container/src/dbus/mod.rs similarity index 100% rename from src/dbus/mod.rs rename to crates/container/src/dbus/mod.rs diff --git a/src/hooks.rs b/crates/container/src/hooks.rs similarity index 100% rename from src/hooks.rs rename to crates/container/src/hooks.rs diff --git a/src/lib.rs b/crates/container/src/lib.rs similarity index 88% rename from src/lib.rs rename to crates/container/src/lib.rs index e41ce1a9..6578b39c 100644 --- a/src/lib.rs +++ b/crates/container/src/lib.rs @@ -1,10 +1,8 @@ pub mod apparmor; pub mod capabilities; -pub mod commands; pub mod container; pub mod dbus; pub mod hooks; -pub mod logger; pub mod namespaces; pub mod notify_socket; pub mod process; diff --git a/src/namespaces.rs b/crates/container/src/namespaces.rs similarity index 100% rename from src/namespaces.rs rename to crates/container/src/namespaces.rs diff --git a/src/notify_socket.rs b/crates/container/src/notify_socket.rs similarity index 100% rename from src/notify_socket.rs rename to crates/container/src/notify_socket.rs diff --git a/src/process/args.rs b/crates/container/src/process/args.rs similarity index 100% rename from src/process/args.rs rename to crates/container/src/process/args.rs diff --git a/src/process/channel.rs b/crates/container/src/process/channel.rs similarity index 100% rename from src/process/channel.rs rename to crates/container/src/process/channel.rs diff --git a/src/process/container_init_process.rs b/crates/container/src/process/container_init_process.rs similarity index 100% rename from src/process/container_init_process.rs rename to crates/container/src/process/container_init_process.rs diff --git a/src/process/container_intermediate_process.rs b/crates/container/src/process/container_intermediate_process.rs similarity index 100% rename from src/process/container_intermediate_process.rs rename to crates/container/src/process/container_intermediate_process.rs diff --git a/src/process/container_main_process.rs b/crates/container/src/process/container_main_process.rs similarity index 100% rename from src/process/container_main_process.rs rename to crates/container/src/process/container_main_process.rs diff --git a/src/process/fork.rs b/crates/container/src/process/fork.rs similarity index 100% rename from src/process/fork.rs rename to crates/container/src/process/fork.rs diff --git a/src/process/message.rs b/crates/container/src/process/message.rs similarity index 100% rename from src/process/message.rs rename to crates/container/src/process/message.rs diff --git a/src/process/mod.rs b/crates/container/src/process/mod.rs similarity index 100% rename from src/process/mod.rs rename to crates/container/src/process/mod.rs diff --git a/src/rootfs/device.rs b/crates/container/src/rootfs/device.rs similarity index 100% rename from src/rootfs/device.rs rename to crates/container/src/rootfs/device.rs diff --git a/src/rootfs/mod.rs b/crates/container/src/rootfs/mod.rs similarity index 100% rename from src/rootfs/mod.rs rename to crates/container/src/rootfs/mod.rs diff --git a/src/rootfs/mount.rs b/crates/container/src/rootfs/mount.rs similarity index 100% rename from src/rootfs/mount.rs rename to crates/container/src/rootfs/mount.rs diff --git a/src/rootfs/rootfs.rs b/crates/container/src/rootfs/rootfs.rs similarity index 100% rename from src/rootfs/rootfs.rs rename to crates/container/src/rootfs/rootfs.rs diff --git a/src/rootfs/symlink.rs b/crates/container/src/rootfs/symlink.rs similarity index 100% rename from src/rootfs/symlink.rs rename to crates/container/src/rootfs/symlink.rs diff --git a/src/rootfs/utils.rs b/crates/container/src/rootfs/utils.rs similarity index 100% rename from src/rootfs/utils.rs rename to crates/container/src/rootfs/utils.rs diff --git a/src/rootless.rs b/crates/container/src/rootless.rs similarity index 100% rename from src/rootless.rs rename to crates/container/src/rootless.rs diff --git a/src/seccomp/fixture/config.json b/crates/container/src/seccomp/fixture/config.json similarity index 100% rename from src/seccomp/fixture/config.json rename to crates/container/src/seccomp/fixture/config.json diff --git a/src/seccomp/mod.rs b/crates/container/src/seccomp/mod.rs similarity index 100% rename from src/seccomp/mod.rs rename to crates/container/src/seccomp/mod.rs diff --git a/src/signal.rs b/crates/container/src/signal.rs similarity index 100% rename from src/signal.rs rename to crates/container/src/signal.rs diff --git a/src/syscall/linux.rs b/crates/container/src/syscall/linux.rs similarity index 100% rename from src/syscall/linux.rs rename to crates/container/src/syscall/linux.rs diff --git a/src/syscall/mod.rs b/crates/container/src/syscall/mod.rs similarity index 100% rename from src/syscall/mod.rs rename to crates/container/src/syscall/mod.rs diff --git a/src/syscall/syscall.rs b/crates/container/src/syscall/syscall.rs similarity index 100% rename from src/syscall/syscall.rs rename to crates/container/src/syscall/syscall.rs diff --git a/src/syscall/test.rs b/crates/container/src/syscall/test.rs similarity index 100% rename from src/syscall/test.rs rename to crates/container/src/syscall/test.rs diff --git a/src/tty.rs b/crates/container/src/tty.rs similarity index 100% rename from src/tty.rs rename to crates/container/src/tty.rs diff --git a/src/utils.rs b/crates/container/src/utils.rs similarity index 100% rename from src/utils.rs rename to crates/container/src/utils.rs diff --git a/src/commands/create.rs b/src/commands/create.rs deleted file mode 100644 index 27f387ea..00000000 --- a/src/commands/create.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Handles the creation of a new container -use anyhow::Result; -use clap::Clap; -use std::path::PathBuf; - -use crate::{container::builder::ContainerBuilder, syscall::syscall::create_syscall}; - -/// Create a container -#[derive(Clap, Debug)] -pub struct Create { - /// File to write pid of the container created - // note that in the end, container is just another process - #[clap(short, long)] - pid_file: Option, - /// path to the bundle directory, containing config.json and root filesystem - #[clap(short, long, default_value = ".")] - bundle: PathBuf, - /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal - #[clap(short, long)] - console_socket: Option, - /// Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) - #[clap(long, default_value = "0")] - preserve_fds: i32, - /// name of the container instance to be started - #[clap(required = true)] - pub container_id: String, -} - -// One thing to note is that in the end, container is just another process in Linux -// it has specific/different control group, namespace, using which program executing in it -// can be given impression that is is running on a complete system, but on the system which -// it is running, it is just another process, and has attributes such as pid, file descriptors, etc. -// associated with it like any other process. -impl Create { - pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { - let syscall = create_syscall(); - ContainerBuilder::new(self.container_id.clone(), syscall.as_ref()) - .with_pid_file(self.pid_file.as_ref()) - .with_console_socket(self.console_socket.as_ref()) - .with_root_path(root_path) - .with_preserved_fds(self.preserve_fds) - .as_init(&self.bundle) - .with_systemd(systemd_cgroup) - .build()?; - - Ok(()) - } -} diff --git a/src/commands/delete.rs b/src/commands/delete.rs deleted file mode 100644 index 1ad46605..00000000 --- a/src/commands/delete.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::commands::load_container; -use anyhow::{Context, Result}; -use clap::Clap; -use std::path::PathBuf; - -/// Release any resources held by the container -#[derive(Clap, Debug)] -pub struct Delete { - #[clap(required = true)] - container_id: String, - /// forces deletion of the container if it is still running (using SIGKILL) - #[clap(short, long)] - force: bool, -} - -impl Delete { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - log::debug!("start deleting {}", self.container_id); - let mut container = load_container(root_path, &self.container_id)?; - container - .delete(self.force) - .with_context(|| format!("failed to delete container {}", self.container_id)) - } -} diff --git a/src/commands/events.rs b/src/commands/events.rs deleted file mode 100644 index 8fde6788..00000000 --- a/src/commands/events.rs +++ /dev/null @@ -1,29 +0,0 @@ -use clap::Clap; -use std::path::PathBuf; - -use anyhow::{Context, Result}; - -use crate::commands::load_container; - -/// Show resource statistics for the container -#[derive(Clap, Debug)] -pub struct Events { - /// Sets the stats collection interval in seconds (default: 5s) - #[clap(long, default_value = "5")] - pub interval: u32, - /// Display the container stats only once - #[clap(long)] - pub stats: bool, - /// Name of the container instance - #[clap(required = true)] - pub container_id: String, -} - -impl Events { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let mut container = load_container(root_path, &self.container_id)?; - container - .events(self.interval, self.stats) - .with_context(|| format!("failed to get events from container {}", self.container_id)) - } -} diff --git a/src/commands/exec.rs b/src/commands/exec.rs deleted file mode 100644 index 41a3acf5..00000000 --- a/src/commands/exec.rs +++ /dev/null @@ -1,70 +0,0 @@ -use anyhow::Result; -use clap::Clap; -use std::{error::Error, path::PathBuf}; - -use crate::{container::builder::ContainerBuilder, syscall::syscall::create_syscall}; - -/// Execute a process within an existing container -#[derive(Clap, Debug)] -pub struct Exec { - /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal - #[clap(long)] - pub console_socket: Option, - #[clap(short, long)] - pub tty: bool, - #[clap(long)] - /// Current working directory of the container - pub cwd: Option, - #[clap(long)] - /// The file to which the pid of the container process should be written to - pub pid_file: Option, - /// Environment variables that should be set in the container - #[clap(short, long, parse(try_from_str = parse_key_val), number_of_values = 1)] - pub env: Vec<(String, String)>, - /// Prevent the process from gaining additional privileges - #[clap(long)] - pub no_new_privs: bool, - /// Path to process.json - #[clap(short, long)] - pub process: Option, - /// Detach from the container process - #[clap(short, long)] - pub detach: bool, - /// Identifier of the container - #[clap(required = true)] - pub container_id: String, - /// Command that should be executed in the container - #[clap(required = false)] - pub command: Vec, -} - -impl Exec { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let syscall = create_syscall(); - ContainerBuilder::new(self.container_id.clone(), syscall.as_ref()) - .with_root_path(root_path) - .with_console_socket(self.console_socket.as_ref()) - .with_pid_file(self.pid_file.as_ref()) - .as_tenant() - .with_cwd(self.cwd.as_ref()) - .with_env(self.env.clone().into_iter().collect()) - .with_process(self.process.as_ref()) - .with_no_new_privs(self.no_new_privs) - .with_process(self.process.as_ref()) - .with_container_args(self.command.clone()) - .build() - } -} - -fn parse_key_val(s: &str) -> Result<(T, U), Box> -where - T: std::str::FromStr, - T::Err: Error + Send + Sync + 'static, - U: std::str::FromStr, - U::Err: Error + Send + Sync + 'static, -{ - let pos = s - .find('=') - .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?; - Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) -} diff --git a/src/commands/info.rs b/src/commands/info.rs deleted file mode 100644 index 3fb29844..00000000 --- a/src/commands/info.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! Contains functions related to printing information about system running Youki -use std::{collections::HashSet, fs, path::Path}; - -use anyhow::Result; -use clap::Clap; -use procfs::{CpuInfo, Meminfo}; - -use cgroups::{self, common::CgroupSetup, v2::controller_type::ControllerType}; - -/// Show information about the system -#[derive(Clap, Debug)] -pub struct Info {} - -impl Info { - pub fn exec(&self) -> Result<()> { - print_youki(); - print_kernel(); - print_os(); - print_hardware(); - print_cgroups(); - print_namespaces(); - - Ok(()) - } -} - -/// print Version of Youki -pub fn print_youki() { - println!("{:<18}{}", "Version", env!("CARGO_PKG_VERSION")); -} - -/// Print Kernel Release, Version and Architecture -pub fn print_kernel() { - let uname = nix::sys::utsname::uname(); - println!("{:<18}{}", "Kernel-Release", uname.release()); - println!("{:<18}{}", "Kernel-Version", uname.version()); - println!("{:<18}{}", "Architecture", uname.machine()); -} - -/// Prints OS Distribution information -// see https://www.freedesktop.org/software/systemd/man/os-release.html -pub fn print_os() { - if let Some(os) = try_read_os_from("/etc/os-release") { - println!("{:<18}{}", "Operating System", os); - } else if let Some(os) = try_read_os_from("/usr/lib/os-release") { - println!("{:<18}{}", "Operating System", os); - } -} - -/// Helper function to read the OS Distribution info -fn try_read_os_from>(path: P) -> Option { - let os_release = path.as_ref(); - if !os_release.exists() { - return None; - } - - if let Ok(release_content) = fs::read_to_string(path) { - let pretty = find_parameter(&release_content, "PRETTY_NAME"); - - if let Some(pretty) = pretty { - return Some(pretty.trim_matches('"').to_owned()); - } - - let name = find_parameter(&release_content, "NAME"); - let version = find_parameter(&release_content, "VERSION"); - - if let Some((name, version)) = name.zip(version) { - return Some(format!( - "{} {}", - name.trim_matches('"'), - version.trim_matches('"') - )); - } - } - - None -} - -/// Helper function to find keyword values in OS info string -fn find_parameter<'a>(content: &'a str, param_name: &str) -> Option<&'a str> { - content - .lines() - .find(|l| l.starts_with(param_name)) - .and_then(|l| l.split_terminator('=').last()) -} - -/// Print Hardware information of system -pub fn print_hardware() { - if let Ok(cpu_info) = CpuInfo::new() { - println!("{:<18}{}", "Cores", cpu_info.num_cores()); - } - - if let Ok(mem_info) = Meminfo::new() { - println!( - "{:<18}{}", - "Total Memory", - mem_info.mem_total / u64::pow(1024, 2) - ); - } -} - -/// Print cgroups info of system -pub fn print_cgroups() { - let cgroup_setup = cgroups::common::get_cgroup_setup(); - if let Ok(cgroup_setup) = &cgroup_setup { - println!("{:<18}{}", "Cgroup setup", cgroup_setup); - } - - println!("Cgroup mounts"); - if let Ok(v1_mounts) = cgroups::v1::util::list_supported_mount_points() { - let mut v1_mounts: Vec = v1_mounts - .iter() - .map(|kv| format!(" {:<16}{}", kv.0.to_string(), kv.1.display())) - .collect(); - - v1_mounts.sort(); - for cgroup_mount in v1_mounts { - println!("{}", cgroup_mount); - } - } - - let unified = cgroups::v2::util::get_unified_mount_point(); - if let Ok(mount_point) = &unified { - println!(" {:<16}{}", "unified", mount_point.display()); - } - - if let Ok(cgroup_setup) = cgroup_setup { - if let Ok(unified) = &unified { - if matches!(cgroup_setup, CgroupSetup::Hybrid | CgroupSetup::Unified) { - if let Ok(controllers) = cgroups::v2::util::get_available_controllers(unified) { - println!("CGroup v2 controllers"); - let active_controllers: HashSet = - controllers.into_iter().collect(); - for controller in cgroups::v2::controller_type::CONTROLLER_TYPES { - let status = if active_controllers.contains(controller) { - "attached" - } else { - "detached" - }; - - println!(" {:<16}{}", controller.to_string(), status); - } - } - - if let Some(config) = read_kernel_config() { - let display = FeatureDisplay::with_status("device", "attached", "detached"); - print_feature_status(&config, "CONFIG_CGROUP_BPF", display); - } - } - } - } -} - -fn read_kernel_config() -> Option { - let uname = nix::sys::utsname::uname(); - let kernel_config = Path::new("/boot").join(format!("config-{}", uname.release())); - if !kernel_config.exists() { - return None; - } - - fs::read_to_string(kernel_config).ok() -} - -pub fn print_namespaces() { - if let Some(content) = read_kernel_config() { - if let Some(ns_enabled) = find_parameter(&content, "CONFIG_NAMESPACES") { - if ns_enabled == "y" { - println!("{:<18}enabled", "Namespaces"); - } else { - println!("{:<18}disabled", "Namespaces"); - return; - } - } - - // mount namespace is always enabled if namespaces are enabled - println!(" {:<16}enabled", "mount"); - print_feature_status(&content, "CONFIG_UTS_NS", FeatureDisplay::new("uts")); - print_feature_status(&content, "CONFIG_IPC_NS", FeatureDisplay::new("ipc")); - print_feature_status(&content, "CONFIG_USER_NS", FeatureDisplay::new("user")); - print_feature_status(&content, "CONFIG_PID_NS", FeatureDisplay::new("pid")); - print_feature_status(&content, "CONFIG_NET_NS", FeatureDisplay::new("network")); - // While the CONFIG_CGROUP_NS kernel feature exists, it is obsolete and should not be used. CGroup namespaces - // are instead enabled with CONFIG_CGROUPS. - print_feature_status(&content, "CONFIG_CGROUPS", FeatureDisplay::new("cgroup")) - } -} - -fn print_feature_status(config: &str, feature: &str, display: FeatureDisplay) { - if let Some(status_flag) = find_parameter(config, feature) { - let status = if status_flag == "y" { - display.enabled - } else { - display.disabled - }; - - println!(" {:<16}{}", display.name, status); - } else { - println!(" {:<16}{}", display.name, display.disabled); - } -} - -struct FeatureDisplay<'a> { - name: &'a str, - enabled: &'a str, - disabled: &'a str, -} - -impl<'a> FeatureDisplay<'a> { - fn new(name: &'a str) -> Self { - Self { - name, - enabled: "enabled", - disabled: "disabled", - } - } - - fn with_status(name: &'a str, enabled: &'a str, disabled: &'a str) -> Self { - Self { - name, - enabled, - disabled, - } - } -} diff --git a/src/commands/kill.rs b/src/commands/kill.rs deleted file mode 100644 index 023260f7..00000000 --- a/src/commands/kill.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Contains functionality of kill container command -use std::{convert::TryInto, path::PathBuf}; - -use anyhow::Result; -use clap::Clap; - -use crate::{commands::load_container, signal::Signal}; - -/// Send the specified signal to the container -#[derive(Clap, Debug)] -pub struct Kill { - #[clap(required = true)] - container_id: String, - signal: String, -} - -impl Kill { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let mut container = load_container(root_path, &self.container_id)?; - let signal: Signal = self.signal.as_str().try_into()?; - container.kill(signal) - } -} diff --git a/src/commands/list.rs b/src/commands/list.rs deleted file mode 100644 index cb4253ff..00000000 --- a/src/commands/list.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! Contains Functionality of list container command -use std::fs; -use std::io; -use std::io::Write; -use std::path::PathBuf; - -use anyhow::Result; -use chrono::{DateTime, Local}; -use clap::Clap; -use tabwriter::TabWriter; - -use crate::container::{state::State, Container}; - -/// List created containers -#[derive(Clap, Debug)] -pub struct List {} - -impl List { - /// lists all existing containers - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let root_path = fs::canonicalize(root_path)?; - let mut content = String::new(); - // all containers' data is stored in their respective dir in root directory - // so we iterate through each and print the various info - for container_dir in fs::read_dir(root_path)? { - let container_dir = container_dir?.path(); - let state_file = State::file_path(&container_dir); - if !state_file.exists() { - continue; - } - - let container = Container::load(container_dir)?; - let pid = if let Some(pid) = container.pid() { - pid.to_string() - } else { - "".to_owned() - }; - - let user_name = container.creator().unwrap_or_default(); - - let created = if let Some(utc) = container.created() { - let local: DateTime = DateTime::from(utc); - local.to_rfc3339_opts(chrono::SecondsFormat::Secs, false) - } else { - "".to_owned() - }; - - content.push_str(&format!( - "{}\t{}\t{}\t{}\t{}\t{}\n", - container.id(), - pid, - container.status(), - container.bundle().display(), - created, - user_name.to_string_lossy() - )); - } - - let mut tab_writer = TabWriter::new(io::stdout()); - writeln!(&mut tab_writer, "ID\tPID\tSTATUS\tBUNDLE\tCREATED\tCREATOR")?; - write!(&mut tab_writer, "{}", content)?; - tab_writer.flush()?; - - Ok(()) - } -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs deleted file mode 100644 index eb545a90..00000000 --- a/src/commands/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -use anyhow::{bail, Context, Result}; -use std::{fs, path::Path}; - -use crate::container::Container; - -pub mod create; -pub mod delete; -pub mod events; -pub mod exec; -pub mod info; -pub mod kill; -pub mod list; -pub mod pause; -pub mod ps; -pub mod resume; -pub mod run; -pub mod spec_json; -pub mod start; -pub mod state; - -fn load_container>(root_path: P, container_id: &str) -> Result { - // resolves relative paths, symbolic links etc. and get complete path - let root_path = fs::canonicalize(&root_path) - .with_context(|| format!("failed to canonicalize {}", root_path.as_ref().display()))?; - // the state of the container is stored in a directory named after the container id - let container_root = root_path.join(container_id); - if !container_root.exists() { - bail!("{} does not exist.", container_id) - } - - Container::load(container_root) - .with_context(|| format!("could not load state for container {}", container_id)) -} diff --git a/src/commands/pause.rs b/src/commands/pause.rs deleted file mode 100644 index 48e39d93..00000000 --- a/src/commands/pause.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Contains functionality of pause container command -use crate::commands::load_container; -use std::path::PathBuf; - -use anyhow::{Context, Result}; -use clap::Clap; - -/// Suspend the processes within the container -#[derive(Clap, Debug)] -pub struct Pause { - #[clap(required = true)] - pub container_id: String, -} - -// Pausing a container indicates suspending all processes in given container -// This uses Freezer cgroup to suspend and resume processes -// For more information see : -// https://man7.org/linux/man-pages/man7/cgroups.7.html -// https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt -impl Pause { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - log::debug!("start pausing container {}", self.container_id); - let mut container = load_container(root_path, &self.container_id)?; - container - .pause() - .with_context(|| format!("failed to pause container {}", self.container_id)) - } -} diff --git a/src/commands/ps.rs b/src/commands/ps.rs deleted file mode 100644 index 1cdb81d3..00000000 --- a/src/commands/ps.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::{container::Container, utils}; -use anyhow::{bail, Context, Result}; -use cgroups; -use clap::{self, Clap}; -use std::{path::PathBuf, process::Command}; - -/// Display the processes inside the container -#[derive(Clap, Debug)] -pub struct Ps { - /// format to display processes: table or json (default: "table") - #[clap(short, long, default_value = "table")] - format: String, - #[clap(required = true)] - pub container_id: String, - /// options will be passed to the ps utility - #[clap(setting = clap::ArgSettings::Last)] - ps_options: Vec, -} -impl Ps { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let container_root = root_path.join(&self.container_id); - if !container_root.exists() { - bail!("{} doesn't exist.", self.container_id) - } - let container = Container::load(container_root)?; - if container.root.exists() { - let config_absolute_path = container.root.join("config.json"); - log::debug!("load spec from {:?}", config_absolute_path); - let spec = oci_spec::runtime::Spec::load(config_absolute_path)?; - log::debug!("spec: {:?}", spec); - let cgroups_path = utils::get_cgroup_path( - spec.linux() - .as_ref() - .context("no linux in spec")? - .cgroups_path(), - container.id(), - ); - let systemd_cgroup = container - .systemd() - .context("could not determine cgroup manager")?; - let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?; - let pids: Vec = cmanager - .get_all_pids()? - .iter() - .map(|pid| pid.as_raw()) - .collect(); - - if self.format == "json" { - println!("{}", serde_json::to_string(&pids)?); - } else if self.format == "table" { - let default_ps_options = vec![String::from("-ef")]; - let ps_options = if self.ps_options.is_empty() { - &default_ps_options - } else { - &self.ps_options - }; - let output = Command::new("ps").args(ps_options).output()?; - if !output.status.success() { - println!("{}", std::str::from_utf8(&output.stderr)?); - } else { - let lines = std::str::from_utf8(&output.stdout)?; - let lines: Vec<&str> = lines.split('\n').collect(); - let pid_index = get_pid_index(lines[0])?; - println!("{}", &lines[0]); - for line in &lines[1..] { - if line.is_empty() { - continue; - } - let fields: Vec<&str> = line.split_whitespace().collect(); - let pid: i32 = fields[pid_index].parse()?; - if pids.contains(&pid) { - println!("{}", line); - } - } - } - } - } - Ok(()) - } -} - -fn get_pid_index(title: &str) -> Result { - let titles = title.split_whitespace(); - - for (index, name) in titles.enumerate() { - if name == "PID" { - return Ok(index); - } - } - bail!("could't find PID field in ps output"); -} diff --git a/src/commands/resume.rs b/src/commands/resume.rs deleted file mode 100644 index b36573b4..00000000 --- a/src/commands/resume.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Contains functionality of resume container command -use std::path::PathBuf; - -use anyhow::{Context, Result}; -use clap::Clap; - -use crate::commands::load_container; - -/// Resume the processes within the container -#[derive(Clap, Debug)] -pub struct Resume { - #[clap(required = true)] - pub container_id: String, -} - -// Resuming a container indicates resuming all processes in given container from paused state -// This uses Freezer cgroup to suspend and resume processes -// For more information see : -// https://man7.org/linux/man-pages/man7/cgroups.7.html -// https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt -impl Resume { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - log::debug!("start resuming container {}", self.container_id); - let mut container = load_container(root_path, &self.container_id)?; - container - .resume() - .with_context(|| format!("failed to resume container {}", self.container_id)) - } -} diff --git a/src/commands/run.rs b/src/commands/run.rs deleted file mode 100644 index 0f2313d0..00000000 --- a/src/commands/run.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::path::PathBuf; - -use crate::container::builder::ContainerBuilder; -use crate::syscall::syscall::create_syscall; -use anyhow::{Context, Result}; -use clap::Clap; - -/// Create a container and immediately start it -#[derive(Clap, Debug)] -pub struct Run { - /// File to write pid of the container created - // note that in the end, container is just another process - #[clap(short, long)] - pid_file: Option, - /// path to the bundle directory, containing config.json and root filesystem - #[clap(short, long, default_value = ".")] - bundle: PathBuf, - /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal - #[clap(short, long)] - console_socket: Option, - /// Pass N additional file descriptors to the container (stdio + $LISTEN_FDS + N in total) - #[clap(long, default_value = "0")] - preserve_fds: i32, - /// name of the container instance to be started - #[clap(required = true)] - pub container_id: String, -} - -impl Run { - pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { - let syscall = create_syscall(); - let mut container = ContainerBuilder::new(self.container_id.clone(), syscall.as_ref()) - .with_pid_file(self.pid_file.as_ref()) - .with_console_socket(self.console_socket.as_ref()) - .with_root_path(root_path) - .with_preserved_fds(self.preserve_fds) - .as_init(&self.bundle) - .with_systemd(systemd_cgroup) - .build()?; - - container - .start() - .with_context(|| format!("failed to start container {}", self.container_id)) - } -} diff --git a/src/commands/spec_json.rs b/src/commands/spec_json.rs deleted file mode 100644 index c26d8187..00000000 --- a/src/commands/spec_json.rs +++ /dev/null @@ -1,122 +0,0 @@ -use anyhow::Result; -use clap::Clap; -use nix; -use oci_spec::runtime::Mount; -use oci_spec::runtime::{ - LinuxBuilder, LinuxIdMappingBuilder, LinuxNamespace, LinuxNamespaceBuilder, LinuxNamespaceType, - Spec, -}; -use serde_json::to_writer_pretty; -use std::fs::File; -use std::path::Path; -use std::path::PathBuf; -/// Command generates a config.json -#[derive(Clap, Debug)] -pub struct SpecJson { - /// Generate a configuration for a rootless container - #[clap(long)] - pub rootless: bool, -} - -pub fn get_default() -> Result { - Ok(Spec::default()) -} - -pub fn get_rootless() -> Result { - // Remove network and user namespace from the default spec - let mut namespaces: Vec = oci_spec::runtime::get_default_namespaces() - .into_iter() - .filter(|ns| { - ns.typ() != LinuxNamespaceType::Network && ns.typ() != LinuxNamespaceType::User - }) - .collect(); - - // Add user namespace - namespaces.push( - LinuxNamespaceBuilder::default() - .typ(LinuxNamespaceType::User) - .build()?, - ); - - let uid = nix::unistd::geteuid().as_raw(); - let gid = nix::unistd::getegid().as_raw(); - - let linux = LinuxBuilder::default() - .namespaces(namespaces) - .uid_mappings(vec![LinuxIdMappingBuilder::default() - .host_id(uid) - .container_id(0_u32) - .size(1_u32) - .build()?]) - .gid_mappings(vec![LinuxIdMappingBuilder::default() - .host_id(gid) - .container_id(0_u32) - .size(1_u32) - .build()?]) - .build()?; - - // Prepare the mounts - - let mut mounts: Vec = oci_spec::runtime::get_default_mounts(); - for mount in &mut mounts { - if mount.destination().eq(Path::new("/sys")) { - mount - .set_source(Some(PathBuf::from("/sys"))) - .set_typ(Some(String::from("none"))) - .set_options(Some(vec![ - "rbind".to_string(), - "nosuid".to_string(), - "noexec".to_string(), - "nodev".to_string(), - "ro".to_string(), - ])); - } else { - let options: Vec = mount - .options() - .as_ref() - .unwrap_or(&vec![]) - .iter() - .filter(|&o| !o.starts_with("gid=") && !o.starts_with("uid=")) - .map(|o| o.to_string()) - .collect(); - mount.set_options(Some(options)); - } - } - - let mut spec = get_default()?; - spec.set_linux(Some(linux)).set_mounts(Some(mounts)); - Ok(spec) -} - -/// spec Cli command -impl SpecJson { - pub fn exec(&self) -> Result<()> { - let spec = if self.rootless { - get_rootless()? - } else { - get_default()? - }; - - // write data to config.json - to_writer_pretty(&File::create("config.json")?, &spec)?; - Ok(()) - } -} - -#[cfg(test)] -// Tests become unstable if not serial. The cause is not known. -mod tests { - use super::*; - use crate::utils::create_temp_dir; - use serial_test::serial; - - #[test] - #[serial] - fn test_spec_json() -> Result<()> { - let spec = get_rootless()?; - let tmpdir = create_temp_dir("test_spec_json").expect("failed to create temp dir"); - let path = tmpdir.path().join("config.json"); - to_writer_pretty(&File::create(path)?, &spec)?; - Ok(()) - } -} diff --git a/src/commands/start.rs b/src/commands/start.rs deleted file mode 100644 index 74b631ec..00000000 --- a/src/commands/start.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Starts execution of the container - -use std::path::PathBuf; - -use anyhow::{Context, Result}; -use clap::Clap; - -use crate::commands::load_container; - -/// Start a previously created container -#[derive(Clap, Debug)] -pub struct Start { - #[clap(required = true)] - pub container_id: String, -} - -impl Start { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let mut container = load_container(root_path, &self.container_id)?; - container - .start() - .with_context(|| format!("failed to start container {}", self.container_id)) - } -} diff --git a/src/commands/state.rs b/src/commands/state.rs deleted file mode 100644 index 28ea4d8c..00000000 --- a/src/commands/state.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -use anyhow::Result; -use clap::Clap; - -use crate::container::Container; - -/// Show the container state -#[derive(Clap, Debug)] -pub struct State { - #[clap(required = true)] - pub container_id: String, -} - -impl State { - pub fn exec(&self, root_path: PathBuf) -> Result<()> { - let root_path = fs::canonicalize(root_path)?; - let container_root = root_path.join(&self.container_id); - let container = Container::load(container_root)?; - println!("{}", serde_json::to_string_pretty(&container.state)?); - std::process::exit(0); - } -} diff --git a/src/logger.rs b/src/logger.rs deleted file mode 100644 index 239b8c41..00000000 --- a/src/logger.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Default Youki Logger - -use std::env; -use std::io::{stderr, Write}; -use std::path::PathBuf; -use std::{ - fs::{File, OpenOptions}, - str::FromStr, -}; - -use anyhow::Result; -use log::{LevelFilter, Log, Metadata, Record}; -use once_cell::sync::OnceCell; - -/// Public global variables to access logger and logfile -pub static YOUKI_LOGGER: OnceCell = OnceCell::new(); -pub static LOG_FILE: OnceCell> = OnceCell::new(); - -/// If in debug mode, default level is debug to get maximum logging -#[cfg(debug_assertions)] -const DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Debug; - -/// If not in debug mode, default level is warn to get important logs -#[cfg(not(debug_assertions))] -const DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Warn; - -/// Initialize the logger, must be called before accessing the logger -/// Multiple parts might call this at once, but the actual initialization -/// is done only once due to use of OnceCell -pub fn init(log_file: Option) -> Result<()> { - // If file exists, ignore, else create and open the file - let _log_file = LOG_FILE.get_or_init(|| -> Option { - // set the log level if specified in env variable or set to default - let level_filter = if let Ok(log_level_str) = env::var("YOUKI_LOG_LEVEL") { - LevelFilter::from_str(&log_level_str).unwrap_or(DEFAULT_LOG_LEVEL) - } else { - DEFAULT_LOG_LEVEL - }; - - // Create a new logger, or get existing if already created - let logger = YOUKI_LOGGER.get_or_init(|| YoukiLogger::new(level_filter.to_level())); - - log::set_logger(logger) - .map(|()| log::set_max_level(level_filter)) - .expect("set logger failed"); - - // Create and open log file - log_file.as_ref().map(|log_file_path| { - OpenOptions::new() - .create(true) - .write(true) - .truncate(false) - .open(log_file_path) - .expect("failed opening log file ") - }) - }); - Ok(()) -} - -/// Youki's custom Logger -pub struct YoukiLogger { - /// Indicates level up to which logs are to be printed - level: Option, -} - -impl YoukiLogger { - /// Create new logger - pub fn new(level: Option) -> Self { - Self { level } - } -} - -/// Implements Log interface given by log crate, so we can use its functionality -impl Log for YoukiLogger { - /// Check if level of given log is enabled or not - fn enabled(&self, metadata: &Metadata) -> bool { - if let Some(level) = self.level { - metadata.level() <= level - } else { - false - } - } - - /// Function to carry out logging - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - let log_msg = match (record.file(), record.line()) { - (Some(file), Some(line)) => format!( - "[{} {}:{}] {} {}\r", - record.level(), - file, - line, - chrono::Local::now().to_rfc3339(), - record.args() - ), - (_, _) => format!( - "[{}] {} {}\r", - record.level(), - chrono::Local::now().to_rfc3339(), - record.args() - ), - }; - - // if log file is set, write to it, else write to stderr - if let Some(mut log_file) = LOG_FILE.get().unwrap().as_ref() { - let _ = writeln!(log_file, "{}", log_msg); - } else { - let _ = writeln!(stderr(), "{}", log_msg); - } - } - } - - /// Flush logs to file - fn flush(&self) { - if let Some(mut log_file) = LOG_FILE.get().unwrap().as_ref() { - log_file.flush().expect("Failed to flush"); - } else { - stderr().flush().expect("Failed to flush"); - } - } -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 8af5d48c..00000000 --- a/src/main.rs +++ /dev/null @@ -1,167 +0,0 @@ -//! # Youki -//! Container Runtime written in Rust, inspired by [railcar](https://github.com/oracle/railcar) -//! This crate provides a container runtime which can be used by a high-level container runtime to run containers. - -use std::fs; -use std::path::PathBuf; - -use anyhow::bail; -use anyhow::Context; -use anyhow::Result; -use clap::{crate_version, Clap}; - -use nix::sys::stat::Mode; -use nix::unistd::getuid; -use youki::commands::create; -use youki::commands::delete; -use youki::commands::events; -use youki::commands::exec; -use youki::commands::info; -use youki::commands::kill; -use youki::commands::list; -use youki::commands::pause; -use youki::commands::ps; -use youki::commands::resume; -use youki::commands::run; -use youki::commands::spec_json; -use youki::commands::start; -use youki::commands::state; -use youki::rootless::rootless_required; -use youki::utils::{self, create_dir_all_with_mode}; - -// High-level commandline option definition -// This takes global options as well as individual commands as specified in [OCI runtime-spec](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md) -// Also check [runc commandline documentation](https://github.com/opencontainers/runc/blob/master/man/runc.8.md) for more explanation -#[derive(Clap, Debug)] -#[clap(version = crate_version!(), author = "youki team")] -struct Opts { - /// root directory to store container state - #[clap(short, long)] - root: Option, - #[clap(short, long)] - log: Option, - #[clap(long)] - log_format: Option, - /// Enable systemd cgroup manager, rather then use the cgroupfs directly. - #[clap(short, long)] - systemd_cgroup: bool, - /// command to actually manage container - #[clap(subcommand)] - subcmd: SubCommand, -} - -// Subcommands accepted by Youki, confirming with [OCI runtime-spec](https://github.com/opencontainers/runtime-spec/blob/master/runtime.md) -// Also for a short information, check [runc commandline documentation](https://github.com/opencontainers/runc/blob/master/man/runc.8.md) -#[derive(Clap, Debug)] -enum SubCommand { - #[clap(version = crate_version!(), author = "youki team")] - Create(create::Create), - #[clap(version = crate_version!(), author = "youki team")] - Start(start::Start), - #[clap(version = crate_version!(), author = "youki team")] - Run(run::Run), - #[clap(version = crate_version!(), author = "youki team")] - Exec(exec::Exec), - #[clap(version = crate_version!(), author = "youki team")] - Kill(kill::Kill), - #[clap(version = crate_version!(), author = "youki team")] - Delete(delete::Delete), - #[clap(version = crate_version!(), author = "youki team")] - State(state::State), - #[clap(version = crate_version!(), author = "youki team")] - Info(info::Info), - #[clap(version = crate_version!(), author = "youki team")] - Spec(spec_json::SpecJson), - #[clap(version = crate_version!(), author = "youki team")] - List(list::List), - #[clap(version = crate_version!(), author = "youki team")] - Pause(pause::Pause), - #[clap(version = crate_version!(), author = "youki team")] - Resume(resume::Resume), - #[clap(version = crate_version!(), author = "youki team")] - Events(events::Events), - #[clap(version = crate_version!(), author = "youki team", setting=clap::AppSettings::AllowLeadingHyphen)] - Ps(ps::Ps), -} - -/// This is the entry point in the container runtime. The binary is run by a high-level container runtime, -/// with various flags passed. This parses the flags, creates and manages appropriate resources. -fn main() -> Result<()> { - // A malicious container can gain access to the host machine by modifying youki's host - // binary and infect it with malicious code. This vulnerability was first discovered - // in runc and was assigned as CVE-2019-5736, but it also affects youki. - // - // The fix is to copy /proc/self/exe in an anonymous file descriptor (created via memfd_create), - // seal it and re-execute it. Because the final step is re-execution, this needs to be done at - // the beginning of this process. - // - // Ref: https://github.com/opencontainers/runc/commit/0a8e4117e7f715d5fbeef398405813ce8e88558b - // Ref: https://github.com/lxc/lxc/commit/6400238d08cdf1ca20d49bafb85f4e224348bf9d - pentacle::ensure_sealed().context("Failed to seal /proc/self/exe")?; - - let opts = Opts::parse(); - - if let Err(e) = youki::logger::init(opts.log) { - eprintln!("log init failed: {:?}", e); - } - - let root_path = determine_root_path(opts.root)?; - let systemd_cgroup = opts.systemd_cgroup; - - match opts.subcmd { - SubCommand::Create(create) => create.exec(root_path, systemd_cgroup), - SubCommand::Start(start) => start.exec(root_path), - SubCommand::Run(run) => run.exec(root_path, systemd_cgroup), - SubCommand::Exec(exec) => exec.exec(root_path), - SubCommand::Kill(kill) => kill.exec(root_path), - SubCommand::Delete(delete) => delete.exec(root_path), - SubCommand::State(state) => state.exec(root_path), - SubCommand::Info(info) => info.exec(), - SubCommand::List(list) => list.exec(root_path), - SubCommand::Spec(spec) => spec.exec(), - SubCommand::Pause(pause) => pause.exec(root_path), - SubCommand::Resume(resume) => resume.exec(root_path), - SubCommand::Events(events) => events.exec(root_path), - SubCommand::Ps(ps) => ps.exec(root_path), - } -} - -fn determine_root_path(root_path: Option) -> Result { - if let Some(path) = root_path { - return Ok(path); - } - - if !rootless_required() { - let default = PathBuf::from("/run/youki"); - utils::create_dir_all(&default)?; - return Ok(default); - } - - // see https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - if let Ok(path) = std::env::var("XDG_RUNTIME_DIR") { - return Ok(PathBuf::from(path)); - } - - // XDG_RUNTIME_DIR is not set, try the usual location - let uid = getuid().as_raw(); - let runtime_dir = PathBuf::from(format!("/run/user/{}", uid)); - if create_dir_all_with_mode(&runtime_dir, uid, Mode::S_IRWXU).is_ok() { - return Ok(runtime_dir); - } - - if let Ok(path) = std::env::var("HOME") { - if let Ok(resolved) = fs::canonicalize(path) { - let run_dir = resolved.join(".youki/run"); - if create_dir_all_with_mode(&run_dir, uid, Mode::S_IRWXU).is_ok() { - return Ok(run_dir); - } - } - } - - let tmp_dir = PathBuf::from(format!("/tmp/youki/{}", uid)); - if create_dir_all_with_mode(&tmp_dir, uid, Mode::S_IRWXU).is_ok() { - return Ok(tmp_dir); - } - - bail!("could not find a storage location with suitable permissions for the current user"); -}