diff --git a/src/cgroups/common.rs b/src/cgroups/common.rs index 20fd04101..02c373dfb 100644 --- a/src/cgroups/common.rs +++ b/src/cgroups/common.rs @@ -8,7 +8,7 @@ use std::{ use anyhow::{bail, Context, Result}; use nix::unistd::Pid; -use oci_spec::LinuxResources; +use oci_spec::{FreezerState, LinuxResources}; use procfs::process::Process; use systemd::daemon::booted; @@ -25,6 +25,8 @@ pub trait CgroupManager { fn apply(&self, linux_resources: &LinuxResources) -> Result<()>; /// Removes the cgroup fn remove(&self) -> Result<()>; + // Sets the freezer cgroup to the specified state + fn freeze(&self, state: FreezerState) -> Result<()>; } #[derive(Debug)] diff --git a/src/cgroups/v1/manager.rs b/src/cgroups/v1/manager.rs index b3aae533c..13e9cc9ad 100644 --- a/src/cgroups/v1/manager.rs +++ b/src/cgroups/v1/manager.rs @@ -19,7 +19,7 @@ use super::{ use crate::cgroups::common::CGROUP_PROCS; use crate::utils; use crate::{cgroups::common::CgroupManager, utils::PathBufExt}; -use oci_spec::LinuxResources; +use oci_spec::{FreezerState, LinuxResources}; pub struct Manager { subsystems: HashMap, } @@ -112,7 +112,7 @@ impl CgroupManager for Manager { CtrlType::Blkio => Blkio::add_task(pid, subsys.1)?, CtrlType::NetworkPriority => NetworkPriority::add_task(pid, subsys.1)?, CtrlType::NetworkClassifier => NetworkClassifier::add_task(pid, subsys.1)?, - _ => continue, + CtrlType::Freezer => Freezer::add_task(pid, subsys.1)?, } } @@ -159,4 +159,15 @@ impl CgroupManager for Manager { Ok(()) } + + fn freeze(&self, state: FreezerState) -> Result<()> { + let linux_resources = LinuxResources { + freezer: Some(state), + ..Default::default() + }; + Freezer::apply( + &linux_resources, + &self.subsystems.get(&CtrlType::Freezer).unwrap(), + ) + } } diff --git a/src/cgroups/v2/manager.rs b/src/cgroups/v2/manager.rs index 32b17779a..92da1a37f 100644 --- a/src/cgroups/v2/manager.rs +++ b/src/cgroups/v2/manager.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::{bail, Result}; use nix::unistd::Pid; -use oci_spec::LinuxResources; +use oci_spec::{FreezerState, LinuxResources}; use super::{ cpu::Cpu, cpuset::CpuSet, freezer::Freezer, hugetlb::HugeTlb, io::Io, memory::Memory, @@ -146,4 +146,12 @@ impl CgroupManager for Manager { Ok(()) } + + fn freeze(&self, state: FreezerState) -> Result<()> { + let linux_resources = LinuxResources { + freezer: Some(state), + ..Default::default() + }; + Freezer::apply(&linux_resources, &self.full_path) + } } diff --git a/src/cgroups/v2/systemd_manager.rs b/src/cgroups/v2/systemd_manager.rs index 874a5b99d..998424a3c 100644 --- a/src/cgroups/v2/systemd_manager.rs +++ b/src/cgroups/v2/systemd_manager.rs @@ -5,7 +5,7 @@ use std::{ use anyhow::{anyhow, bail, Result}; use nix::unistd::Pid; -use oci_spec::LinuxResources; +use oci_spec::{FreezerState, LinuxResources}; use std::path::{Path, PathBuf}; use super::{ @@ -247,6 +247,14 @@ impl CgroupManager for SystemDCGroupManager { fn remove(&self) -> Result<()> { Ok(()) } + + fn freeze(&self, state: FreezerState) -> Result<()> { + let linux_resources = LinuxResources { + freezer: Some(state), + ..Default::default() + }; + Freezer::apply(&linux_resources, &self.full_path) + } } #[cfg(test)] diff --git a/src/container/container.rs b/src/container/container.rs index f24a50dff..72bbe12bf 100644 --- a/src/container/container.rs +++ b/src/container/container.rs @@ -7,6 +7,7 @@ use chrono::DateTime; use nix::unistd::Pid; use chrono::Utc; +use oci_spec::Spec; use procfs::process::Process; use crate::command::syscall::create_syscall; @@ -56,7 +57,9 @@ impl Container { match proc.stat.state().unwrap() { ProcState::Zombie | ProcState::Dead => ContainerStatus::Stopped, _ => match self.status() { - ContainerStatus::Creating | ContainerStatus::Created => self.status(), + ContainerStatus::Creating + | ContainerStatus::Created + | ContainerStatus::Paused => self.status(), _ => ContainerStatus::Running, }, } @@ -98,6 +101,14 @@ impl Container { self.state.status == ContainerStatus::Running } + pub fn can_pause(&self) -> bool { + self.state.status.can_pause() + } + + pub fn can_resume(&self) -> bool { + self.state.status.can_resume() + } + pub fn pid(&self) -> Option { self.state.pid.map(Pid::from_raw) } @@ -169,4 +180,8 @@ impl Container { root: container_root, }) } + + pub fn spec(&self) -> Result { + Spec::load(self.root.join("config.json")) + } } diff --git a/src/container/state.rs b/src/container/state.rs index c77ad1fd5..6b294f557 100644 --- a/src/container/state.rs +++ b/src/container/state.rs @@ -22,6 +22,8 @@ pub enum ContainerStatus { Running, // The container process has exited Stopped, + // The container process has paused + Paused, } impl ContainerStatus { @@ -33,13 +35,21 @@ impl ContainerStatus { use ContainerStatus::*; match self { Creating | Stopped => false, - Created | Running => true, + Created | Running | Paused => true, } } pub fn can_delete(&self) -> bool { matches!(self, ContainerStatus::Stopped) } + + pub fn can_pause(&self) -> bool { + matches!(self, ContainerStatus::Running) + } + + pub fn can_resume(&self) -> bool { + matches!(self, ContainerStatus::Paused) + } } impl Display for ContainerStatus { @@ -49,6 +59,7 @@ impl Display for ContainerStatus { Self::Created => "Created", Self::Running => "Running", Self::Stopped => "Stopped", + Self::Paused => "Paused", }; write!(f, "{}", print) diff --git a/src/lib.rs b/src/lib.rs index 439f53d4d..0e6f21b6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,10 @@ pub mod list; pub mod logger; pub mod namespaces; pub mod notify_socket; +pub mod pause; pub mod pipe; pub mod process; +pub mod resume; pub mod rootfs; pub mod rootless; pub mod signal; diff --git a/src/main.rs b/src/main.rs index b9aea21a3..a07837738 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ use youki::exec; use youki::info; use youki::kill; use youki::list; +use youki::pause; +use youki::resume; use youki::rootless::should_use_rootless; use youki::start; use youki::state; @@ -59,6 +61,10 @@ enum SubCommand { Info(info::Info), #[clap(version = "0.0.1", author = "utam0k ")] List(list::List), + #[clap(version = "0.0.1", author = "utam0k ")] + Pause(pause::Pause), + #[clap(version = "0.0.1", author = "utam0k ")] + Resume(resume::Resume), } /// This is the entry point in the container runtime. The binary is run by a high-level container runtime, @@ -88,5 +94,7 @@ fn main() -> Result<()> { SubCommand::State(state) => state.exec(root_path), SubCommand::Info(info) => info.exec(), SubCommand::List(list) => list.exec(root_path), + SubCommand::Pause(pause) => pause.exec(root_path, systemd_cgroup), + SubCommand::Resume(resume) => return resume.exec(root_path, systemd_cgroup), } } diff --git a/src/pause.rs b/src/pause.rs new file mode 100644 index 000000000..737d30127 --- /dev/null +++ b/src/pause.rs @@ -0,0 +1,49 @@ +//! Contains functionality of pause container command +use std::fs::canonicalize; +use std::path::PathBuf; + +use anyhow::{bail, Result}; +use clap::Clap; + +use crate::cgroups; +use crate::container::Container; +use crate::container::ContainerStatus; +use crate::utils; +use oci_spec::FreezerState; + +#[derive(Clap, Debug)] +pub struct Pause { + pub container_id: String, +} + +impl Pause { + pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { + log::debug!("start pausing container {}", self.container_id); + let root_path = canonicalize(root_path)?; + 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)?.refresh_status()?; + if !container.can_pause() { + bail!( + "{} could not be paused because it was {:?}", + self.container_id, + container.status() + ); + } + + let spec = container.spec()?; + let cgroups_path = + utils::get_cgroup_path(&spec.linux.unwrap().cgroups_path, &self.container_id); + let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?; + cmanager.freeze(FreezerState::Frozen)?; + + log::debug!("saving paused status"); + container.update_status(ContainerStatus::Paused).save()?; + + log::debug!("container {} paused", self.container_id); + Ok(()) + } +} diff --git a/src/resume.rs b/src/resume.rs new file mode 100644 index 000000000..9735f81e5 --- /dev/null +++ b/src/resume.rs @@ -0,0 +1,49 @@ +//! Contains functionality of resume container command +use std::fs::canonicalize; +use std::path::PathBuf; + +use anyhow::{bail, Result}; +use clap::Clap; + +use crate::cgroups; +use crate::container::Container; +use crate::container::ContainerStatus; +use crate::utils; +use oci_spec::FreezerState; + +#[derive(Clap, Debug)] +pub struct Resume { + pub container_id: String, +} + +impl Resume { + pub fn exec(&self, root_path: PathBuf, systemd_cgroup: bool) -> Result<()> { + log::debug!("start resuming container {}", self.container_id); + let root_path = canonicalize(root_path)?; + 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)?.refresh_status()?; + if !container.can_resume() { + bail!( + "{} could not be resumed because it was {:?}", + self.container_id, + container.status() + ); + } + + let spec = container.spec()?; + let cgroups_path = + utils::get_cgroup_path(&spec.linux.unwrap().cgroups_path, &self.container_id); + let cmanager = cgroups::common::create_cgroup_manager(cgroups_path, systemd_cgroup)?; + cmanager.freeze(FreezerState::Thawed)?; + + log::debug!("saving running status"); + container.update_status(ContainerStatus::Running).save()?; + + log::debug!("container {} resumed", self.container_id); + Ok(()) + } +}