#[macro_use]
extern crate log;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
pub mod blkio;
pub mod cgroup;
pub mod cpu;
pub mod cpuacct;
pub mod cpuset;
pub mod devices;
pub mod error;
pub mod freezer;
pub mod hierarchies;
pub mod hugetlb;
pub mod memory;
pub mod net_cls;
pub mod net_prio;
pub mod perf_event;
pub mod pid;
pub mod rdma;
pub mod cgroup_builder;
use blkio::BlkIoController;
use cpu::CpuController;
use cpuacct::CpuAcctController;
use cpuset::CpuSetController;
use devices::DevicesController;
use error::*;
use freezer::FreezerController;
use hugetlb::HugeTlbController;
use memory::MemController;
use net_cls::NetClsController;
use net_prio::NetPrioController;
use perf_event::PerfEventController;
use pid::PidController;
use rdma::RdmaController;
pub use cgroup::Cgroup;
#[derive(Debug)]
pub enum Subsystem {
Pid(PidController),
Mem(MemController),
CpuSet(CpuSetController),
CpuAcct(CpuAcctController),
Cpu(CpuController),
Devices(DevicesController),
Freezer(FreezerController),
NetCls(NetClsController),
BlkIo(BlkIoController),
PerfEvent(PerfEventController),
NetPrio(NetPrioController),
HugeTlb(HugeTlbController),
Rdma(RdmaController),
}
#[doc(hidden)]
#[derive(Eq, PartialEq, Debug)]
pub enum Controllers {
Pids,
Mem,
CpuSet,
CpuAcct,
Cpu,
Devices,
Freezer,
NetCls,
BlkIo,
PerfEvent,
NetPrio,
HugeTlb,
Rdma,
}
impl Controllers {
pub fn to_string(&self) -> String {
match self {
Controllers::Pids => return "pids".to_string(),
Controllers::Mem => return "memory".to_string(),
Controllers::CpuSet => return "cpuset".to_string(),
Controllers::CpuAcct => return "cpuacct".to_string(),
Controllers::Cpu => return "cpu".to_string(),
Controllers::Devices => return "devices".to_string(),
Controllers::Freezer => return "freezer".to_string(),
Controllers::NetCls => return "net_cls".to_string(),
Controllers::BlkIo => return "blkio".to_string(),
Controllers::PerfEvent => return "perf_event".to_string(),
Controllers::NetPrio => return "net_prio".to_string(),
Controllers::HugeTlb => return "hugetlb".to_string(),
Controllers::Rdma => return "rdma".to_string(),
}
}
}
mod sealed {
use super::*;
pub trait ControllerInternal {
fn apply(&self, res: &Resources) -> Result<()>;
fn control_type(&self) -> Controllers;
fn get_path(&self) -> &PathBuf;
fn get_path_mut(&mut self) -> &mut PathBuf;
fn get_base(&self) -> &PathBuf;
fn verify_path(&self) -> Result<()> {
if self.get_path().starts_with(self.get_base()) {
Ok(())
} else {
Err(Error::new(ErrorKind::InvalidPath))
}
}
fn open_path(&self, p: &str, w: bool) -> Result<File> {
let mut path = self.get_path().clone();
path.push(p);
self.verify_path()?;
if w {
match File::create(&path) {
Err(e) => return Err(Error::with_cause(ErrorKind::WriteFailed, e)),
Ok(file) => return Ok(file),
}
} else {
match File::open(&path) {
Err(e) => return Err(Error::with_cause(ErrorKind::ReadFailed, e)),
Ok(file) => return Ok(file),
}
}
}
#[doc(hidden)]
fn path_exists(&self, p: &str) -> bool {
if let Err(_) = self.verify_path() {
return false;
}
std::path::Path::new(p).exists()
}
}
}
pub(crate) use sealed::ControllerInternal;
pub trait Controller {
#[doc(hidden)]
fn control_type(&self) -> Controllers;
fn path(&self) -> &Path;
fn apply(&self, res: &Resources) -> Result<()>;
fn create(&self);
fn exists(&self) -> bool;
fn delete(&self);
fn add_task(&self, pid: &CgroupPid) -> Result<()>;
fn tasks(&self) -> Vec<CgroupPid>;
}
impl<T> Controller for T where T: ControllerInternal {
fn control_type(&self) -> Controllers {
ControllerInternal::control_type(self)
}
fn path(&self) -> &Path {
self.get_path()
}
fn apply(&self, res: &Resources) -> Result<()> {
ControllerInternal::apply(self, res)
}
fn create(&self) {
self.verify_path().expect("path should be valid");
match ::std::fs::create_dir(self.get_path()) {
Ok(_) => (),
Err(e) => warn!("error create_dir {:?}", e),
}
}
fn exists(&self) -> bool {
self.get_path().exists()
}
fn delete(&self) {
if self.get_path().exists() {
let _ = ::std::fs::remove_dir(self.get_path());
}
}
fn add_task(&self, pid: &CgroupPid) -> Result<()> {
self.open_path("tasks", true).and_then(|mut file| {
file.write_all(pid.pid.to_string().as_ref())
.map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e))
})
}
fn tasks(&self) -> Vec<CgroupPid> {
self.open_path("tasks", false)
.and_then(|file| {
let bf = BufReader::new(file);
let mut v = Vec::new();
for line in bf.lines() {
if let Ok(line) = line {
let n = line.trim().parse().unwrap_or(0u64);
v.push(n);
}
}
Ok(v.into_iter().map(CgroupPid::from).collect())
}).unwrap_or(vec![])
}
}
#[doc(hidden)]
pub trait ControllIdentifier {
fn controller_type() -> Controllers;
}
pub trait Hierarchy {
fn subsystems(&self) -> Vec<Subsystem>;
fn root(&self) -> PathBuf;
fn root_control_group(&self) -> Cgroup;
#[doc(hidden)]
fn check_support(&self, sub: Controllers) -> bool;
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct MemoryResources {
pub update_values: bool,
pub kernel_memory_limit: u64,
pub memory_hard_limit: u64,
pub memory_soft_limit: u64,
pub kernel_tcp_memory_limit: u64,
pub memory_swap_limit: u64,
pub swappiness: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct PidResources {
pub update_values: bool,
pub maximum_number_of_processes: pid::PidMax,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct CpuResources {
pub update_values: bool,
pub cpus: String,
pub mems: String,
pub shares: u64,
pub quota: i64,
pub period: u64,
pub realtime_runtime: i64,
pub realtime_period: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct DeviceResource {
pub allow: bool,
pub devtype: ::devices::DeviceType,
pub major: i64,
pub minor: i64,
pub access: Vec<::devices::DevicePermissions>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct DeviceResources {
pub update_values: bool,
pub devices: Vec<DeviceResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NetworkPriority {
pub name: String,
pub priority: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct NetworkResources {
pub update_values: bool,
pub class_id: u64,
pub priorities: Vec<NetworkPriority>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct HugePageResource {
pub size: String,
pub limit: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct HugePageResources {
pub update_values: bool,
pub limits: Vec<HugePageResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoDeviceResource {
pub major: u64,
pub minor: u64,
pub weight: u16,
pub leaf_weight: u16,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoDeviceThrottleResource {
pub major: u64,
pub minor: u64,
pub rate: u64,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct BlkIoResources {
pub update_values: bool,
pub weight: u16,
pub leaf_weight: u16,
pub weight_device: Vec<BlkIoDeviceResource>,
pub throttle_read_bps_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_read_iops_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_write_bps_device: Vec<BlkIoDeviceThrottleResource>,
pub throttle_write_iops_device: Vec<BlkIoDeviceThrottleResource>,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Resources {
pub memory: MemoryResources,
pub pid: PidResources,
pub cpu: CpuResources,
pub devices: DeviceResources,
pub network: NetworkResources,
pub hugepages: HugePageResources,
pub blkio: BlkIoResources,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct CgroupPid {
pub pid: u64,
}
impl From<u64> for CgroupPid {
fn from(u: u64) -> CgroupPid {
CgroupPid { pid: u }
}
}
impl<'a> From<&'a std::process::Child> for CgroupPid {
fn from(u: &std::process::Child) -> CgroupPid {
CgroupPid { pid: u.id() as u64 }
}
}
impl Subsystem {
fn enter(self, path: &Path) -> Self {
match self {
Subsystem::Pid(cont) => Subsystem::Pid({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Mem(cont) => Subsystem::Mem({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::CpuSet(cont) => Subsystem::CpuSet({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::CpuAcct(cont) => Subsystem::CpuAcct({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Cpu(cont) => Subsystem::Cpu({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Devices(cont) => Subsystem::Devices({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Freezer(cont) => Subsystem::Freezer({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::NetCls(cont) => Subsystem::NetCls({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::BlkIo(cont) => Subsystem::BlkIo({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::PerfEvent(cont) => Subsystem::PerfEvent({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::NetPrio(cont) => Subsystem::NetPrio({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::HugeTlb(cont) => Subsystem::HugeTlb({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
Subsystem::Rdma(cont) => Subsystem::Rdma({
let mut c = cont.clone();
c.get_path_mut().push(path);
c
}),
}
}
fn to_controller(&self) -> &dyn Controller {
match self {
Subsystem::Pid(cont) => cont,
Subsystem::Mem(cont) => cont,
Subsystem::CpuSet(cont) => cont,
Subsystem::CpuAcct(cont) => cont,
Subsystem::Cpu(cont) => cont,
Subsystem::Devices(cont) => cont,
Subsystem::Freezer(cont) => cont,
Subsystem::NetCls(cont) => cont,
Subsystem::BlkIo(cont) => cont,
Subsystem::PerfEvent(cont) => cont,
Subsystem::NetPrio(cont) => cont,
Subsystem::HugeTlb(cont) => cont,
Subsystem::Rdma(cont) => cont,
}
}
}