diff --git a/src/cgroups/v2/systemd_manager.rs b/src/cgroups/v2/systemd_manager.rs index 6e01e3050..a21b65bf6 100644 --- a/src/cgroups/v2/systemd_manager.rs +++ b/src/cgroups/v2/systemd_manager.rs @@ -202,7 +202,7 @@ impl SystemDCGroupManager { Ok(controllers) } - fn write_controllers(path: &Path, controllers: &Vec) -> Result<()> { + fn write_controllers(path: &Path, controllers: &[String]) -> Result<()> { for controller in controllers { common::write_cgroup_file_str(path.join(CGROUP_SUBTREE_CONTROL), controller)?; } diff --git a/src/delete.rs b/src/delete.rs new file mode 100644 index 000000000..7b535bace --- /dev/null +++ b/src/delete.rs @@ -0,0 +1,67 @@ +use std::fs; +use std::path::Path; +use std::path::PathBuf; + +use anyhow::{bail, Result}; +use clap::Clap; + +use crate::cgroups; +use crate::container::Container; +use crate::utils; + +#[derive(Clap, Debug)] +pub struct Delete { + container_id: String, + // forces deletion of the container. + #[clap(short, long)] + force: bool, +} + +impl Delete { + pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { + log::debug!("start deleting {}", self.container_id); + // state of container is stored in a directory named as container id inside + // root directory given in commandline options + let container_root = root_path.join(&self.container_id); + if !container_root.exists() { + bail!("{} doesn't exist.", self.container_id) + } + // load container state from json file, and check status of the container + // it might be possible that delete is invoked on a running container. + log::debug!("load the container from {:?}", container_root); + let container = Container::load(container_root)?.refresh_status()?; + if container.can_delete() { + if container.root.exists() { + nix::unistd::chdir(&PathBuf::from(&container.state.bundle))?; + let config_absolute_path = &PathBuf::from(&container.state.bundle) + .join(Path::new("config.json")) + .to_string_lossy() + .to_string(); + log::debug!("load spec from {:?}", config_absolute_path); + let spec = oci_spec::Spec::load(config_absolute_path)?; + log::debug!("spec: {:?}", spec); + + // remove the directory storing container state + log::debug!("remove dir {:?}", container.root); + fs::remove_dir_all(&container.root)?; + + let cgroups_path = + utils::get_cgroup_path(&spec.linux.unwrap().cgroups_path, container.id()); + + // remove the cgroup created for the container + // check https://man7.org/linux/man-pages/man7/cgroups.7.html + // creating and removing cgroups section for more information on cgroups + let cmanager = + cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?; + cmanager.remove()?; + } + std::process::exit(0) + } else { + bail!( + "{} could not be deleted because it was {:?}", + container.id(), + container.status() + ) + } + } +} diff --git a/src/info.rs b/src/info.rs index b726e25d5..087b7bbee 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,8 +1,26 @@ -use procfs::{CpuInfo, Meminfo}; use std::{fs, path::Path}; +use anyhow::Result; +use clap::Clap; +use procfs::{CpuInfo, Meminfo}; + use crate::cgroups; +#[derive(Clap, Debug)] +pub struct Info {} + +impl Info { + pub fn exec(&self) -> Result<()> { + print_youki(); + print_kernel(); + print_os(); + print_hardware(); + print_cgroups(); + + Ok(()) + } +} + pub fn print_youki() { println!("{:<18}{}", "Version", env!("CARGO_PKG_VERSION")); } diff --git a/src/kill.rs b/src/kill.rs new file mode 100644 index 000000000..9c0eebb2d --- /dev/null +++ b/src/kill.rs @@ -0,0 +1,46 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{bail, Result}; +use clap::Clap; +use nix::sys::signal as nix_signal; + +use crate::{ + container::{Container, ContainerStatus}, + signal, +}; + +#[derive(Clap, Debug)] +pub struct Kill { + container_id: String, + signal: String, +} + +impl Kill { + pub fn exec(&self, root_path: PathBuf) -> Result<()> { + // resolves relative paths, symbolic links etc. and get complete path + let root_path = fs::canonicalize(root_path)?; + // state of container is stored in a directory named as container id inside + // root directory given in commandline options + let container_root = root_path.join(&self.container_id); + if !container_root.exists() { + bail!("{} doesn't exist.", self.container_id) + } + + // load container state from json file, and check status of the container + // it might be possible that kill is invoked on a already stopped container etc. + let container = Container::load(container_root)?.refresh_status()?; + if container.can_kill() { + let sig = signal::from_str(self.signal.as_str())?; + log::debug!("kill signal {} to {}", sig, container.pid().unwrap()); + nix_signal::kill(container.pid().unwrap(), sig)?; + container.update_status(ContainerStatus::Stopped).save()?; + std::process::exit(0) + } else { + bail!( + "{} could not be killed because it was {:?}", + container.id(), + container.status() + ) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b8509f676..da3c23572 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,10 @@ pub mod command; pub mod container; pub mod create; pub mod dbus; +pub mod delete; pub mod info; +pub mod kill; +pub mod list; pub mod logger; pub mod namespaces; pub mod notify_socket; @@ -18,6 +21,7 @@ pub mod rootfs; pub mod rootless; pub mod signal; pub mod start; +pub mod state; pub mod stdio; pub mod tty; pub mod utils; diff --git a/src/list.rs b/src/list.rs new file mode 100644 index 000000000..e99fd0245 --- /dev/null +++ b/src/list.rs @@ -0,0 +1,67 @@ +use std::ffi::OsString; +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::Container; + +#[derive(Clap, Debug)] +pub struct List {} + +impl List { + pub fn exec(&self, root_path: PathBuf) -> Result<()> { + let root_path = fs::canonicalize(root_path)?; + let mut content = String::new(); + + for container_dir in fs::read_dir(root_path)? { + let container_dir = container_dir?.path(); + let state_file = container_dir.join("state.json"); + if !state_file.exists() { + continue; + } + + let container = Container::load(container_dir)?.refresh_status()?; + let pid = if let Some(pid) = container.pid() { + pid.to_string() + } else { + "".to_owned() + }; + + let user_name = if let Some(creator) = container.creator() { + creator + } else { + OsString::new() + }; + + 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(), + 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/main.rs b/src/main.rs index 5acec9290..b244d09f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,31 +2,21 @@ //! 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::ffi::OsString; - use std::fs; -use std::io; -use std::io::Write; - -use std::path::{Path, PathBuf}; +use std::path::PathBuf; -use anyhow::{bail, Result}; -use chrono::{DateTime, Local}; +use anyhow::Result; use clap::Clap; -use nix::sys::signal as nix_signal; use youki::command::linux::LinuxCommand; - -use youki::container::{Container, ContainerStatus}; use youki::create; -use youki::info::{print_cgroups, print_hardware, print_kernel, print_os, print_youki}; +use youki::delete; +use youki::info; +use youki::kill; +use youki::list; use youki::rootless::should_use_rootless; -use youki::signal; use youki::start; - -use tabwriter::TabWriter; -use youki::cgroups; -use youki::utils; +use youki::state; /// 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) @@ -49,25 +39,6 @@ struct Opts { subcmd: SubCommand, } -#[derive(Clap, Debug)] -pub struct Kill { - container_id: String, - signal: String, -} - -#[derive(Clap, Debug)] -pub struct Delete { - container_id: String, - // forces deletion of the container. - #[clap(short, long)] - force: bool, -} - -#[derive(Clap, Debug)] -pub struct StateArgs { - pub container_id: String, -} - /// 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)] @@ -77,15 +48,15 @@ enum SubCommand { #[clap(version = "0.0.1", author = "utam0k ")] Start(start::Start), #[clap(version = "0.0.1", author = "utam0k ")] - Kill(Kill), + Kill(kill::Kill), #[clap(version = "0.0.1", author = "utam0k ")] - Delete(Delete), + Delete(delete::Delete), #[clap(version = "0.0.1", author = "utam0k ")] - State(StateArgs), + State(state::State), #[clap(version = "0.0.1", author = "utam0k ")] - Info, + Info(info::Info), #[clap(version = "0.0.1", author = "utam0k ")] - List, + List(list::List), } /// This is the entry point in the container runtime. The binary is run by a high-level container runtime, @@ -109,145 +80,10 @@ fn main() -> Result<()> { match opts.subcmd { SubCommand::Create(create) => create.exec(root_path, systemd_cgroup, LinuxCommand), SubCommand::Start(start) => start.exec(root_path), - SubCommand::Kill(kill) => { - // resolves relative paths, symbolic links etc. and get complete path - let root_path = fs::canonicalize(root_path)?; - // state of container is stored in a directory named as container id inside - // root directory given in commandline options - let container_root = root_path.join(&kill.container_id); - if !container_root.exists() { - bail!("{} doesn't exist.", kill.container_id) - } - - // load container state from json file, and check status of the container - // it might be possible that kill is invoked on a already stopped container etc. - let container = Container::load(container_root)?.refresh_status()?; - if container.can_kill() { - let sig = signal::from_str(kill.signal.as_str())?; - log::debug!("kill signal {} to {}", sig, container.pid().unwrap()); - nix_signal::kill(container.pid().unwrap(), sig)?; - container.update_status(ContainerStatus::Stopped).save()?; - std::process::exit(0) - } else { - bail!( - "{} could not be killed because it was {:?}", - container.id(), - container.status() - ) - } - } - SubCommand::Delete(delete) => { - log::debug!("start deleting {}", delete.container_id); - // state of container is stored in a directory named as container id inside - // root directory given in commandline options - let container_root = root_path.join(&delete.container_id); - if !container_root.exists() { - bail!("{} doesn't exist.", delete.container_id) - } - // load container state from json file, and check status of the container - // it might be possible that delete is invoked on a running container. - log::debug!("load the container from {:?}", container_root); - let container = Container::load(container_root)?.refresh_status()?; - if container.can_delete() { - if container.root.exists() { - nix::unistd::chdir(&PathBuf::from(&container.state.bundle))?; - let config_absolute_path = &PathBuf::from(&container.state.bundle) - .join(Path::new("config.json")) - .to_string_lossy() - .to_string(); - log::debug!("load spec from {:?}", config_absolute_path); - let spec = oci_spec::Spec::load(config_absolute_path)?; - log::debug!("spec: {:?}", spec); - - // remove the directory storing container state - log::debug!("remove dir {:?}", container.root); - fs::remove_dir_all(&container.root)?; - - let cgroups_path = - utils::get_cgroup_path(&spec.linux.unwrap().cgroups_path, container.id()); - - // remove the cgroup created for the container - // check https://man7.org/linux/man-pages/man7/cgroups.7.html - // creating and removing cgroups section for more information on cgroups - let cmanager = - cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?; - cmanager.remove()?; - } - std::process::exit(0) - } else { - bail!( - "{} could not be deleted because it was {:?}", - container.id(), - container.status() - ) - } - } - SubCommand::State(state_args) => { - let root_path = fs::canonicalize(root_path)?; - let container_root = root_path.join(state_args.container_id); - let container = Container::load(container_root)?.refresh_status()?; - println!("{}", serde_json::to_string_pretty(&container.state)?); - std::process::exit(0); - } - - SubCommand::Info => { - print_youki(); - print_kernel(); - print_os(); - print_hardware(); - print_cgroups(); - - Ok(()) - } - - SubCommand::List => { - let root_path = fs::canonicalize(root_path)?; - let mut content = String::new(); - - for container_dir in fs::read_dir(root_path)? { - let container_dir = container_dir?.path(); - let state_file = container_dir.join("state.json"); - if !state_file.exists() { - continue; - } - - let container = Container::load(container_dir)?.refresh_status()?; - let pid = if let Some(pid) = container.pid() { - pid.to_string() - } else { - "".to_owned() - }; - - let user_name = if let Some(creator) = container.creator() { - creator - } else { - OsString::new() - }; - - 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(), - 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(()) - } + SubCommand::Kill(kill) => kill.exec(root_path), + SubCommand::Delete(delete) => delete.exec(root_path, systemd_cgroup), + SubCommand::State(state) => state.exec(root_path), + SubCommand::Info(info) => info.exec(), + SubCommand::List(list) => list.exec(root_path), } } diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 000000000..7be62193c --- /dev/null +++ b/src/state.rs @@ -0,0 +1,22 @@ +use std::fs; +use std::path::PathBuf; + +use anyhow::Result; +use clap::Clap; + +use crate::container::Container; + +#[derive(Clap, Debug)] +pub struct State { + 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)?.refresh_status()?; + println!("{}", serde_json::to_string_pretty(&container.state)?); + std::process::exit(0); + } +}