use std::path::PathBuf; use std::fs::File; use std::io::{BufRead, BufReader, Write}; pub mod hierarchies; pub mod pid; pub mod memory; pub mod cpuset; pub mod cpuacct; pub mod cpu; pub mod devices; pub mod cgroup; pub mod freezer; pub mod net_cls; pub mod blkio; pub mod perf_event; pub mod net_prio; pub mod hugetlb; pub mod rdma; use pid::PidController; use memory::MemController; use cpuset::CpuSetController; use cpuacct::CpuAcctController; use cpu::CpuController; use freezer::FreezerController; use devices::DevicesController; use net_cls::NetClsController; use blkio::BlkIoController; use perf_event::PerfEventController; use net_prio::NetPrioController; use hugetlb::HugeTlbController; use rdma::RdmaController; pub use cgroup::Cgroup; /// Contains all the subsystems that are available in this crate. #[derive(Debug)] pub enum Subsystem { /// Controller for the `Pid` subsystem, see `PidController` for more information. Pid(PidController), /// Controller for the `Mem` subsystem, see `MemController` for more information. Mem(MemController), /// Controller for the `CpuSet subsystem, see `CpuSetController` for more information. CpuSet(CpuSetController), /// Controller for the `CpuAcct` subsystem, see `CpuAcctController` for more information. CpuAcct(CpuAcctController), /// Controller for the `Cpu` subsystem, see `CpuController` for more information. Cpu(CpuController), /// Controller for the `Devices` subsystem, see `DevicesController` for more information. Devices(DevicesController), /// Controller for the `Freezer` subsystem, see `FreezerController` for more information. Freezer(FreezerController), /// Controller for the `NetCls` subsystem, see `NetClsController` for more information. NetCls(NetClsController), /// Controller for the `BlkIo` subsystem, see `BlkIoController` for more information. BlkIo(BlkIoController), /// Controller for the `PerfEvent` subsystem, see `PerfEventController` for more information. PerfEvent(PerfEventController), /// Controller for the `NetPrio` subsystem, see `NetPrioController` for more information. NetPrio(NetPrioController), /// Controller for the `HugeTlb` subsystem, see `HugeTlbController` for more information. HugeTlb(HugeTlbController), /// Controller for the `Rdma` subsystem, see `RdmaController` for more information. Rdma(RdmaController), } /// The different types of errors that can occur while manipulating control groups. #[derive(Debug)] pub enum CgroupError { /// An error occured while writing to a control group file. WriteError(std::io::Error), /// An error occured while trying to read from a control group file. ReadError(std::io::Error), /// An error occured while trying to parse a value from a control group file. /// /// In the future, there will be some information attached to this field. ParseError, /// You tried to do something invalid. /// /// This could be because you tried to set a value in a control group that is not a root /// control group. Or, when using unified hierarchy, you tried to add a task in a leaf node. InvalidOperation, /// The path of the control group was invalid. /// /// This could be caused by trying to escape the control group filesystem via a string of "..". /// This crate checks against this and operations will fail with this error. InvalidPath, /// An unknown error has occured. Unknown, } impl PartialEq for CgroupError { fn eq(&self, other: &CgroupError) -> bool { match self { CgroupError::WriteError(_) => if let CgroupError::WriteError(_) = other { return true; } else { return false }, CgroupError::ReadError(_) => if let CgroupError::ReadError(_) = other { return true; } else { return false }, CgroupError::ParseError => if let CgroupError::ParseError = other { return true; } else { return false }, CgroupError::InvalidOperation => if let CgroupError::InvalidOperation = other { return true; } else { return false }, CgroupError::InvalidPath => if let CgroupError::InvalidPath = other { return true; } else { return false }, CgroupError::Unknown => if let CgroupError::Unknown = other { return true; } else { return false }, } } } #[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(), } } } /// A Controller is a subsystem attached to the control group. /// /// Implementors are able to control certain aspects of a control group. pub trait Controller { /// Apply a set of resources to the Controller, invoking its internal functions to pass the /// kernel the information. fn apply(&self, res: &Resources) -> Result<(), CgroupError>; /* meta stuff */ #[doc(hidden)] fn control_type(&self) -> Controllers; #[doc(hidden)] fn get_path(&self) -> &PathBuf; #[doc(hidden)] fn get_path_mut(&mut self) -> &mut PathBuf; #[doc(hidden)] fn get_base(&self) -> &PathBuf; #[doc(hidden)] fn verify_path(&self) -> bool { self.get_path().starts_with(self.get_base()) } /// Create this controller fn create(&self) { if self.verify_path() { match ::std::fs::create_dir(self.get_path()) { Ok(_) => (), Err(e) => println!("error create_dir {:?}", e), } } } /// Does this controller already exist? fn exists(&self) -> bool { self.get_path().exists() } /// Delete the controller. fn delete(&self) { if self.get_path().exists() { let _ = ::std::fs::remove_dir(self.get_path()); } } #[doc(hidden)] fn open_path(&self, p: &str, w: bool) -> Result { let mut path = self.get_path().clone(); path.push(p); if !self.verify_path() { return Err(CgroupError::InvalidPath); } if w { match File::create(&path) { Err(e) => return Err(CgroupError::WriteError(e)), Ok(file) => return Ok(file), } } else { match File::open(&path) { Err(e) => return Err(CgroupError::ReadError(e)), Ok(file) => return Ok(file), } } } #[doc(hidden)] fn path_exists(&self, p: &str) -> bool { if !self.verify_path() { return false; } std::path::Path::new(p).exists() } /// Attach a task to this controller. fn add_task(&self, pid: &CgroupPid) -> Result<(), CgroupError> { self.open_path("tasks", true).and_then(|mut file| { file.write_all(pid.pid.to_string().as_ref()).map_err(CgroupError::WriteError) }) } /// Get the list of tasks that this controller has. fn tasks(&self) -> Vec { 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; } /// Control group hierarchy (right now, only V1 is supported, but in the future Unified will be /// implemented as well). pub trait Hierarchy { /// Returns what subsystems are supported by the hierarchy. fn subsystems(&self) -> Vec; /// Returns the root directory of the hierarchy. fn root(&self) -> PathBuf; /// Return a handle to the root control group in the hierarchy. fn root_control_group(&self) -> Cgroup; /// Checks whether a certain subsystem is supported in the hierarchy. /// /// This is an internal function and should not be used. #[doc(hidden)] fn check_support(&self, sub: Controllers) -> bool; } /// Resource limits for the memory subsystem. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct MemoryResources { /// Whether values should be applied to the controller. pub update_values: bool, /// How much memory (in bytes) can the kernel consume. pub kernel_memory_limit: u64, /// Upper limit of memory usage of the control group's tasks. pub memory_hard_limit: u64, /// How much memory the tasks in the control group can use when the system is under memory /// pressure. pub memory_soft_limit: u64, /// How much of the kernel's memory (in bytes) can be used for TCP-related buffers. pub kernel_tcp_memory_limit: u64, /// How much memory and swap together can the tasks in the control group use. pub memory_swap_limit: u64, /// Controls the tendency of the kernel to swap out parts of the address space of the tasks to /// disk. Lower value implies less likely. /// /// Note, however, that a value of zero does not mean the process is never swapped out. Use the /// traditional `mlock(2)` system call for that purpose. pub swappiness: u64, } /// Resources limits on the number of processes. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct PidResources { /// Whether values should be applied to the controller. pub update_values: bool, /// The maximum number of processes that can exist in the control group. /// /// Note that attaching processes to the control group will still succeed _even_ if the limit /// would be violated, however forks/clones inside the control group will have with `EAGAIN` if /// they would violate the limit set here. pub maximum_number_of_processes: pid::PidMax, } /// Resources limits about how the tasks can use the CPU. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct CpuResources { /// Whether values should be applied to the controller. pub update_values: bool, /* cpuset */ /// A comma-separated list of CPU IDs where the task in the control group can run. Dashes /// between numbers indicate ranges. pub cpus: String, /// Same syntax as the `cpus` field of this structure, but applies to memory nodes instead of /// processors. pub mems: String, /* cpu */ /// Weight of how much of the total CPU time should this control group get. Note that this is /// hierarchical, so this is weighted against the siblings of this control group. pub shares: u64, /// In one `period`, how much can the tasks run in nanoseconds. pub quota: i64, /// Period of time in nanoseconds. pub period: u64, /// This is currently a no-operation. pub realtime_runtime: i64, /// This is currently a no-operation. pub realtime_period: u64, } /// A device resource that can be allowed or denied access to. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct DeviceResource { /// If true, access to the device is allowed, otherwise it's denied. pub allow: bool, /// `'c'` for character device, `'b'` for block device; or `'a'` for all devices. pub devtype: ::devices::DeviceType, /// The major number of the device. pub major: i64, /// The minor number of the device. pub minor: i64, /// Sequence of `'r'`, `'w'` or `'m'`, each denoting read, write or mknod permissions. pub access: Vec<::devices::DevicePermissions>, } /// Limit the usage of devices for the control group's tasks. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct DeviceResources { /// Whether values should be applied to the controller. pub update_values: bool, /// For each device in the list, the limits in the structure are applied. pub devices: Vec, } /// Assigned priority for a network device. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct NetworkPriority { /// The name (as visible in `ifconfig`) of the interface. pub name: String, /// Assigned priority. pub priority: u64, } /// Collections of limits and tags that can be imposed on packets emitted by the tasks in the /// control group. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct NetworkResources { /// Whether values should be applied to the controller. pub update_values: bool, /// The networking class identifier to attach to the packets. /// /// This can then later be used in iptables and such to have special rules. pub class_id: u64, /// Priority of the egress traffic for each interface. pub priorities: Vec, } /// A hugepage type and its consumption limit for the control group. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct HugePageResource { /// The size of the hugepage, i.e. `2MB`, `1GB`, etc. pub size: String, /// The amount of bytes (of memory consumed by the tasks) that are allowed to be backed by /// hugepages. pub limit: u64, } /// Provides the ability to set consumption limit on each type of hugepages. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct HugePageResources { /// Whether values should be applied to the controller. pub update_values: bool, /// Set a limit of consumption for each hugepages type. pub limits: Vec, } /// Weight for a particular block device. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct BlkIoDeviceResource { /// The major number of the device. pub major: u64, /// The minor number of the device. pub minor: u64, /// The weight of the device against the descendant nodes. pub weight: u16, /// The weight of the device against the sibling nodes. pub leaf_weight: u16, } /// Provides the ability to throttle a device (both byte/sec, and IO op/s) #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct BlkIoDeviceThrottleResource { /// The major number of the device. pub major: u64, /// The minor number of the device. pub minor: u64, /// The rate. pub rate: u64, } /// General block I/O resource limits. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct BlkIoResources { /// Whether values should be applied to the controller. pub update_values: bool, /// The weight of the control group against descendant nodes. pub weight: u16, /// The weight of the control group against sibling nodes. pub leaf_weight: u16, /// For each device, a separate weight (both normal and leaf) can be provided. pub weight_device: Vec, /// Throttled read bytes/second can be provided for each device. pub throttle_read_bps_device: Vec, /// Throttled read IO operations per second can be provided for each device. pub throttle_read_iops_device: Vec, /// Throttled written bytes/second can be provided for each device. pub throttle_write_bps_device: Vec, /// Throttled write IO operations per second can be provided for each device. pub throttle_write_iops_device: Vec, } /// The resource limits and constraints that will be set on the control group. #[derive(Debug, Clone, Eq, PartialEq, Default)] pub struct Resources { /// Memory usage related limits. pub memory: MemoryResources, /// Process identifier related limits. pub pid: PidResources, /// CPU related limits. pub cpu: CpuResources, /// Device related limits. pub devices: DeviceResources, /// Network related tags and limits. pub network: NetworkResources, /// Hugepages consumption related limits. pub hugepages: HugePageResources, /// Block device I/O related limits. pub blkio: BlkIoResources, } /// A structure representing a `pid`. Currently implementations exist for `u64` and /// `std::process::Child`. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct CgroupPid { /// The process identifier pub pid: u64, } impl From 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: &String) -> 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, } } }