1
0
Fork 0
mirror of https://github.com/containers/youki synced 2024-06-03 13:26:13 +02:00
youki/cgroups/src/common.rs

230 lines
6.7 KiB
Rust
Raw Normal View History

2021-05-29 17:15:16 +02:00
use std::{
env,
2021-05-29 17:15:16 +02:00
fmt::{Debug, Display},
2021-07-29 16:49:38 +02:00
fs::{self, File},
io::{BufRead, BufReader, Write},
2021-05-29 17:15:16 +02:00
path::{Path, PathBuf},
};
2021-06-11 17:29:55 +02:00
use anyhow::{bail, Context, Result};
2021-05-25 19:32:52 +02:00
use nix::unistd::Pid;
2021-07-17 18:25:10 +02:00
use oci_spec::{FreezerState, LinuxResources};
2021-05-29 17:15:16 +02:00
use procfs::process::Process;
#[cfg(feature = "systemd_cgroups")]
use systemd::daemon::booted;
#[cfg(not(feature = "systemd_cgroups"))]
fn booted() -> Result<bool> {
bail!("This build does not include the systemd cgroups feature")
}
2021-05-25 19:32:52 +02:00
2021-08-01 22:49:31 +02:00
use super::v1;
use super::v2;
2021-05-29 14:45:50 +02:00
2021-07-25 19:53:11 +02:00
use super::stats::Stats;
2021-06-03 22:21:51 +02:00
pub const CGROUP_PROCS: &str = "cgroup.procs";
2021-05-26 21:32:21 +02:00
pub const DEFAULT_CGROUP_ROOT: &str = "/sys/fs/cgroup";
2021-05-25 19:32:52 +02:00
pub trait CgroupManager {
/// Adds a task specified by its pid to the cgroup
fn add_task(&self, pid: Pid) -> Result<()>;
/// Applies resource restrictions to the cgroup
fn apply(&self, linux_resources: &LinuxResources) -> Result<()>;
/// Removes the cgroup
2021-05-29 15:03:50 +02:00
fn remove(&self) -> Result<()>;
2021-07-17 18:25:10 +02:00
// Sets the freezer cgroup to the specified state
fn freeze(&self, state: FreezerState) -> Result<()>;
2021-07-25 19:53:11 +02:00
/// Retrieve statistics for the cgroup
fn stats(&self) -> Result<Stats>;
2021-07-29 16:49:38 +02:00
// Gets the PIDs inside the cgroup
fn get_all_pids(&self) -> Result<Vec<Pid>>;
2021-05-25 19:32:52 +02:00
}
2021-05-25 17:06:13 +02:00
#[derive(Debug)]
2021-05-25 17:06:13 +02:00
pub enum Cgroup {
V1,
V2,
}
2021-05-29 14:45:50 +02:00
impl Display for Cgroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let print = match *self {
Cgroup::V1 => "v1",
Cgroup::V2 => "v2",
};
write!(f, "{}", print)
}
}
#[inline]
2021-06-04 15:34:07 +02:00
pub fn write_cgroup_file_str<P: AsRef<Path>>(path: P, data: &str) -> Result<()> {
2021-05-26 21:32:21 +02:00
fs::OpenOptions::new()
2021-05-29 17:15:16 +02:00
.create(false)
.write(true)
.truncate(false)
2021-06-11 19:10:17 +02:00
.open(path.as_ref())
.with_context(|| format!("failed to open {:?}", path.as_ref()))?
.write_all(data.as_bytes())
.with_context(|| format!("failed to write to {:?}", path.as_ref()))?;
2021-05-26 21:32:21 +02:00
Ok(())
}
2021-06-03 22:21:51 +02:00
#[inline]
2021-06-04 15:34:07 +02:00
pub fn write_cgroup_file<P: AsRef<Path>, T: ToString>(path: P, data: T) -> Result<()> {
2021-06-03 22:21:51 +02:00
fs::OpenOptions::new()
.create(false)
.write(true)
.truncate(false)
2021-06-11 19:10:17 +02:00
.open(path.as_ref())
.with_context(|| format!("failed to open {:?}", path.as_ref()))?
.write_all(data.to_string().as_bytes())
.with_context(|| format!("failed to write to {:?}", path.as_ref()))?;
2021-06-03 22:21:51 +02:00
Ok(())
}
2021-07-25 19:53:11 +02:00
#[inline]
pub fn read_cgroup_file<P: AsRef<Path>>(path: P) -> Result<String> {
let path = path.as_ref();
fs::read_to_string(path).with_context(|| format!("failed to open {:?}", path))
}
2021-06-11 17:29:55 +02:00
pub fn get_supported_cgroup_fs() -> Result<Vec<Cgroup>> {
let cgroup_mount = Process::myself()?
.mountinfo()?
.into_iter()
.find(|m| m.fs_type == "cgroup");
let cgroup2_mount = Process::myself()?
.mountinfo()?
.into_iter()
2021-06-11 17:29:55 +02:00
.find(|m| m.fs_type == "cgroup2");
let mut cgroups = vec![];
if cgroup_mount.is_some() {
cgroups.push(Cgroup::V1);
}
if cgroup2_mount.is_some() {
cgroups.push(Cgroup::V2);
}
Ok(cgroups)
}
pub fn create_cgroup_manager<P: Into<PathBuf>>(
cgroup_path: P,
systemd_cgroup: bool,
) -> Result<Box<dyn CgroupManager>> {
let cgroup_mount = Process::myself()?
2021-05-29 17:24:05 +02:00
.mountinfo()?
.into_iter()
.find(|m| m.fs_type == "cgroup");
let cgroup2_mount = Process::myself()?
2021-05-29 17:15:16 +02:00
.mountinfo()?
.into_iter()
.find(|m| m.fs_type == "cgroup2");
2021-05-29 14:45:50 +02:00
match (cgroup_mount, cgroup2_mount) {
(Some(_), None) => {
log::info!("cgroup manager V1 will be used");
2021-05-31 15:58:05 +02:00
Ok(Box::new(v1::manager::Manager::new(cgroup_path.into())?))
}
(None, Some(cgroup2)) => {
log::info!("cgroup manager V2 will be used");
if systemd_cgroup {
if !booted()? {
bail!("systemd cgroup flag passed, but systemd support for managing cgroups is not available");
}
log::info!("systemd cgroup manager will be used");
return Ok(Box::new(v2::SystemDCGroupManager::new(
cgroup2.mount_point,
cgroup_path.into(),
)?));
}
2021-05-31 15:58:05 +02:00
Ok(Box::new(v2::manager::Manager::new(
cgroup2.mount_point,
cgroup_path.into(),
2021-05-31 15:58:05 +02:00
)?))
}
(Some(_), Some(cgroup2)) => {
let cgroup_override = env::var("YOUKI_PREFER_CGROUPV2");
match cgroup_override {
Ok(v) if v == "true" => {
log::info!("cgroup manager V2 will be used");
if systemd_cgroup {
if !booted()? {
bail!("systemd cgroup flag passed, but systemd support for managing cgroups is not available");
}
log::info!("systemd cgroup manager will be used");
return Ok(Box::new(v2::SystemDCGroupManager::new(
cgroup2.mount_point,
cgroup_path.into(),
)?));
}
2021-05-31 15:58:05 +02:00
Ok(Box::new(v2::manager::Manager::new(
cgroup2.mount_point,
cgroup_path.into(),
2021-05-31 15:58:05 +02:00
)?))
}
2021-06-04 15:34:07 +02:00
_ => {
log::info!("cgroup manager V1 will be used");
Ok(Box::new(v1::manager::Manager::new(cgroup_path.into())?))
}
2021-06-03 22:21:51 +02:00
}
}
2021-05-31 15:58:05 +02:00
_ => bail!("could not find cgroup filesystem"),
2021-05-29 14:45:50 +02:00
}
2021-05-29 17:15:16 +02:00
}
2021-07-29 16:49:38 +02:00
pub fn get_all_pids(path: &Path) -> Result<Vec<Pid>> {
log::debug!("scan pids in folder: {:?}", path);
let mut result = vec![];
walk_dir(path, &mut |p| {
2021-07-29 16:49:38 +02:00
let file_path = p.join(CGROUP_PROCS);
if file_path.exists() {
let file = File::open(file_path)?;
for line in BufReader::new(file).lines().flatten() {
result.push(Pid::from_raw(line.parse::<i32>()?))
2021-07-29 16:49:38 +02:00
}
}
Ok(())
})?;
Ok(result)
}
fn walk_dir<F>(path: &Path, c: &mut F) -> Result<()>
where
F: FnMut(&Path) -> Result<()>,
{
c(path)?;
2021-07-29 16:49:38 +02:00
for entry in fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
walk_dir(&path, c)?;
}
}
Ok(())
}
2021-08-01 22:49:31 +02:00
pub trait PathBufExt {
fn join_safely(&self, p: &Path) -> Result<PathBuf>;
}
impl PathBufExt for PathBuf {
fn join_safely(&self, p: &Path) -> Result<PathBuf> {
if !p.is_absolute() && !p.as_os_str().is_empty() {
bail!(
"cannot join {:?} because it is not the absolute path.",
p.display()
)
}
Ok(PathBuf::from(format!("{}{}", self.display(), p.display())))
}
}