diff --git a/src/blkio.rs b/src/blkio.rs index a585dc4..fcd0c5e 100644 --- a/src/blkio.rs +++ b/src/blkio.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::io::{Read, Write}; use std::fs::File; -use {BlkIoResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, BlkIoResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use CgroupError::*; /// A controller that allows controlling the `blkio` subsystem of a Cgroup. /// @@ -105,28 +106,28 @@ impl Controller for BlkIoController { let res: &BlkIoResources = &res.blkio; if res.update_values { - self.set_weight(res.weight as u64); - self.set_leaf_weight(res.leaf_weight as u64); + let _ = self.set_weight(res.weight as u64); + let _ = self.set_leaf_weight(res.leaf_weight as u64); for dev in &res.weight_device { - self.set_weight_for_device(format!("{}:{} {}", + let _ = self.set_weight_for_device(format!("{}:{} {}", dev.major, dev.minor, dev.weight)); } for dev in &res.throttle_read_bps_device { - self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate); + let _ = self.throttle_read_bps_for_device(dev.major, dev.minor, dev.rate); } for dev in &res.throttle_write_bps_device { - self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate); + let _ = self.throttle_write_bps_for_device(dev.major, dev.minor, dev.rate); } for dev in &res.throttle_read_iops_device { - self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate); + let _ = self.throttle_read_iops_for_device(dev.major, dev.minor, dev.rate); } for dev in &res.throttle_write_iops_device { - self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate); + let _ = self.throttle_write_iops_for_device(dev.major, dev.minor, dev.rate); } } } @@ -152,16 +153,20 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController { } } -fn read_string_from(mut file: File) -> Option { +fn read_string_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) + match file.read_to_string(&mut string) { + Ok(_) => Ok(string.trim().to_string()), + Err(e) => Err(CgroupError::ReadError(e)), + } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl BlkIoController { @@ -270,68 +275,68 @@ impl BlkIoController { /// Set the leaf weight on the control group's tasks, i.e., how are they weighted against the /// descendant control groups' tasks. - pub fn set_leaf_weight(self: &Self, w: u64) { + pub fn set_leaf_weight(self: &Self, w: u64) -> Result<(), CgroupError> { self.open_path("blkio.leaf_weight", true).and_then(|mut file| { - file.write_all(w.to_string().as_ref()).ok() - }); + file.write_all(w.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Same as `set_leaf_weight()`, but settable per each block device. - pub fn set_leaf_weight_for_device(self: &Self, d: String) { + pub fn set_leaf_weight_for_device(self: &Self, d: String) -> Result<(), CgroupError> { self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| { - file.write_all(d.as_ref()).ok() - }); + file.write_all(d.as_ref()).map_err(CgroupError::WriteError) + }) } /// Reset the statistics the kernel has gathered so far and start fresh. - pub fn reset_stats(self: &Self) { + pub fn reset_stats(self: &Self) -> Result<(), CgroupError> { self.open_path("blkio.leaf_weight_device", true).and_then(|mut file| { - file.write_all("1".to_string().as_ref()).ok() - }); + file.write_all("1".to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Throttle the bytes per second rate of read operation affecting the block device /// `major:minor` to `bps`. - pub fn throttle_read_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) { + pub fn throttle_read_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) -> Result<(), CgroupError> { self.open_path("blkio.throttle.read_bps_device", true).and_then(|mut file| { - file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).ok() - }); + file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Throttle the I/O operations per second rate of read operation affecting the block device /// `major:minor` to `bps`. - pub fn throttle_read_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) { + pub fn throttle_read_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) -> Result<(), CgroupError> { self.open_path("blkio.throttle.read_iops_device", true).and_then(|mut file| { - file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).ok() - }); + file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Throttle the bytes per second rate of write operation affecting the block device /// `major:minor` to `bps`. - pub fn throttle_write_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) { + pub fn throttle_write_bps_for_device(self: &Self, major: u64, minor: u64, bps: u64) -> Result<(), CgroupError> { self.open_path("blkio.throttle.write_bps_device", true).and_then(|mut file| { - file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).ok() - }); + file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Throttle the I/O operations per second rate of write operation affecting the block device /// `major:minor` to `bps`. - pub fn throttle_write_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) { + pub fn throttle_write_iops_for_device(self: &Self, major: u64, minor: u64, iops: u64) -> Result<(), CgroupError> { self.open_path("blkio.throttle.write_iops_device", true).and_then(|mut file| { - file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).ok() - }); + file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Set the weight of the control group's tasks. - pub fn set_weight(self: &Self, w: u64) { + pub fn set_weight(self: &Self, w: u64) -> Result<(), CgroupError> { self.open_path("blkio.leaf_weight", true).and_then(|mut file| { - file.write_all(w.to_string().as_ref()).ok() - }); + file.write_all(w.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Same as `set_weight()`, but settable per each block device. - pub fn set_weight_for_device(self: &Self, d: String) { + pub fn set_weight_for_device(self: &Self, d: String) -> Result<(), CgroupError> { self.open_path("blkio.weight_device", true).and_then(|mut file| { - file.write_all(d.as_ref()).ok() - }); + file.write_all(d.as_ref()).map_err(CgroupError::WriteError) + }) } } diff --git a/src/cgroup.rs b/src/cgroup.rs index 3b4a379..5f858dc 100644 --- a/src/cgroup.rs +++ b/src/cgroup.rs @@ -1,6 +1,6 @@ //! This module handles cgroup operations. Start here! -use {CgroupPid, Resources, ControllIdentifier, Controller, Hierarchy, Subsystem}; +use {CgroupError, CgroupPid, Resources, ControllIdentifier, Controller, Hierarchy, Subsystem}; use std::convert::From; @@ -136,12 +136,12 @@ impl<'b> Cgroup<'b> { /// Note that this means that the task will be moved back to the root control group in the /// hierarchy and any rules applied to that control group will _still_ apply to the task. pub fn remove_task(self: &Self, pid: CgroupPid) { - self.hier.root_control_group().add_task(pid); + let _ = self.hier.root_control_group().add_task(pid); } /// Attach a task to the control group. - pub fn add_task(self: &Self, pid: CgroupPid) { - self.subsystems().iter().for_each(|sub| sub.to_controller().add_task(&pid)); + pub fn add_task(self: &Self, pid: CgroupPid) -> Result<(), CgroupError> { + self.subsystems().iter().try_for_each(|sub| sub.to_controller().add_task(&pid)) } /// Returns an Iterator that can be used to iterate over the tasks that are currently in the diff --git a/src/cpu.rs b/src/cpu.rs index 9cf90e1..2d6d74c 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; use std::io::{Read, Write}; -use {CpuResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, CpuResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; /// A controller that allows controlling the `cpu` subsystem of a Cgroup. /// @@ -40,9 +40,9 @@ impl Controller for CpuController { if res.update_values { /* apply pid_max */ - self.set_shares(res.shares); - self.set_cfs_period(res.period); - self.set_cfs_quota(res.quota as u64); + let _ = self.set_shares(res.shares); + let _ = self.set_cfs_period(res.period); + let _ = self.set_cfs_quota(res.quota as u64); /* TODO: rt properties (CONFIG_RT_GROUP_SCHED) are not yet supported */ } } @@ -84,8 +84,11 @@ impl CpuController { Cpu { stat: self.open_path("cpu.stat", false).and_then(|mut file| { let mut s = String::new(); - let _ = file.read_to_string(&mut s); - Some(s) + let res = file.read_to_string(&mut s); + match res { + Ok(_) => Ok(s), + Err(e) => Err(CgroupError::ReadError(e)), + } }).unwrap_or("".to_string()), } } @@ -96,25 +99,25 @@ impl CpuController { /// For example, setting control group `A`'s `shares` to `100`, and control group `B`'s /// `shares` to `200` ensures that control group `B` receives twice as much as CPU bandwidth. /// (Assuming both `A` and `B` are of the same parent) - pub fn set_shares(self: &Self, shares: u64) { + pub fn set_shares(self: &Self, shares: u64) -> Result<(), CgroupError> { self.open_path("cpu.shares", true).and_then(|mut file| { - file.write_all(shares.to_string().as_ref()).ok() - }); + file.write_all(shares.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Specify a period (when using the CFS scheduler) of time in microseconds for how often this /// control group's access to the CPU should be reallocated. - pub fn set_cfs_period(self: &Self, us: u64) { + pub fn set_cfs_period(self: &Self, us: u64) -> Result<(), CgroupError> { self.open_path("cpu.cfs_period_us", true).and_then(|mut file| { - file.write_all(us.to_string().as_ref()).ok() - }); + file.write_all(us.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Specify a quota (when using the CFS scheduler) of time in microseconds for which all tasks /// in this control group can run during one period (see: `set_cfs_period()`). - pub fn set_cfs_quota(self: &Self, us: u64) { + pub fn set_cfs_quota(self: &Self, us: u64) -> Result<(), CgroupError> { self.open_path("cpu.cfs_quota_us", true).and_then(|mut file| { - file.write_all(us.to_string().as_ref()).ok() - }); + file.write_all(us.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } } diff --git a/src/cpuacct.rs b/src/cpuacct.rs index 6321ea6..851b107 100644 --- a/src/cpuacct.rs +++ b/src/cpuacct.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; use std::io::{Read, Write}; use std::fs::File; -use {Controllers, Resources, Subsystem, ControllIdentifier, Controller}; +use {CgroupError, Controllers, Resources, Subsystem, ControllIdentifier, Controller}; /// A controller that allows controlling the `cpuacct` subsystem of a Cgroup. /// @@ -79,10 +79,24 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + let res = file.read_to_string(&mut string); + match res { + Ok(_) => match string.trim().parse() { + Ok(e) => Ok(e), + Err(_) => Err(CgroupError::ParseError), + }, + Err(e) => Err(CgroupError::ReadError(e)), + } +} + +fn read_string_from(mut file: File) -> Result { + let mut string = String::new(); + match file.read_to_string(&mut string) { + Ok(_) => Ok(string.trim().to_string()), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl CpuAcctController { @@ -101,38 +115,18 @@ impl CpuAcctController { pub fn cpuacct(self: &Self) -> CpuAcct { CpuAcct { stat: self.open_path("cpuacct.stat", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(|file| read_string_from(file)).unwrap_or("".to_string()), usage: self.open_path("cpuacct.usage", false) .and_then(|file| read_u64_from(file)) .unwrap_or(0), usage_all: self.open_path("cpuacct.usage_all", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(|file| read_string_from(file)).unwrap_or("".to_string()), usage_percpu: self.open_path("cpuacct.usage_percpu", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(|file| read_string_from(file)).unwrap_or("".to_string()), usage_percpu_sys: self.open_path("cpuacct.usage_percpu_sys", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(|file| read_string_from(file)).unwrap_or("".to_string()), usage_percpu_user: self.open_path("cpuacct.usage_percpu_user", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(|file| read_string_from(file)).unwrap_or("".to_string()), usage_sys: self.open_path("cpuacct.usage_sys", false) .and_then(|file| read_u64_from(file)) .unwrap_or(0), @@ -143,9 +137,9 @@ impl CpuAcctController { } /// Reset the statistics the kernel has gathered about the control group. - pub fn reset(self: &Self) { + pub fn reset(self: &Self) -> Result<(), CgroupError> { self.open_path("cpuacct.usage", true).and_then(|mut file| { - file.write_all(b"0").ok() - }); + file.write_all(b"0").map_err(CgroupError::WriteError) + }) } } diff --git a/src/cpuset.rs b/src/cpuset.rs index b96e11f..2748680 100644 --- a/src/cpuset.rs +++ b/src/cpuset.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::io::{Read, Write}; use std::fs::File; -use {CpuResources, Resources, Controller, ControllIdentifier, Subsystem, Controllers}; +use {CgroupError, CpuResources, Resources, Controller, ControllIdentifier, Subsystem, Controllers}; +use CgroupError::*; /// A controller that allows controlling the `cpuset` subsystem of a Cgroup. /// @@ -84,8 +85,8 @@ impl Controller for CpuSetController { if res.update_values { /* apply pid_max */ - self.set_cpus(&res.cpus); - self.set_mems(&res.mems); + let _ = self.set_cpus(&res.cpus); + let _ = self.set_mems(&res.mems); } } } @@ -110,10 +111,20 @@ impl<'a> From<&'a Subsystem> for &'a CpuSetController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_string_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => Ok(string.trim().to_string()), + Err(e) => Err(CgroupError::ReadError(e)), + } +} + +fn read_u64_from(mut file: File) -> Result { + let mut string = String::new(); + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl CpuSetController { @@ -137,122 +148,96 @@ impl CpuSetController { }).map(|x| x == 1).unwrap_or(false) }, cpus: { - self.open_path("cpuset.cpus", false).and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap() + self.open_path("cpuset.cpus", false).and_then(read_string_from).unwrap_or("".to_string()) }, effective_cpus: { - self.open_path("cpuset.effective_cpus", false).and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap() + self.open_path("cpuset.effective_cpus", false).and_then(read_string_from).unwrap_or("".to_string()) }, effective_mems: { - self.open_path("cpuset.effective_mems", false).and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap() + self.open_path("cpuset.effective_mems", false).and_then(read_string_from).unwrap_or("".to_string()) }, mem_exclusive: { - self.open_path("cpuset.mem_exclusive", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.mem_exclusive", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, mem_hardwall: { - self.open_path("cpuset.mem_hardwall", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.mem_hardwall", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, memory_migrate: { - self.open_path("cpuset.memory_migrate", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.memory_migrate", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, memory_pressure: { - self.open_path("cpuset.memory_pressure", false).and_then(|file| { - read_u64_from(file) - }).unwrap_or(0) + self.open_path("cpuset.memory_pressure", false).and_then(read_u64_from).unwrap_or(0) }, memory_pressure_enabled: { - self.open_path("cpuset.memory_pressure_enabled", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1) + self.open_path("cpuset.memory_pressure_enabled", false).and_then(read_u64_from) + .map(|x| x == 1).ok() }, memory_spread_page: { - self.open_path("cpuset.memory_spread_page", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.memory_spread_page", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, memory_spread_slab: { - self.open_path("cpuset.memory_spread_slab", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.memory_spread_slab", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, mems: { - self.open_path("cpuset.mems", false).and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap() + self.open_path("cpuset.mems", false).and_then(read_string_from).unwrap_or("".to_string()) }, sched_load_balance: { - self.open_path("cpuset.sched_load_balance", false).and_then(|file| { - read_u64_from(file) - }).map(|x| x == 1).unwrap_or(false) + self.open_path("cpuset.sched_load_balance", false).and_then(read_u64_from) + .map(|x| x == 1).unwrap_or(false) }, sched_relax_domain_level: { - self.open_path("cpuset.sched_relax_domain_level", false).and_then(|file| { - read_u64_from(file) - }).unwrap_or(0) + self.open_path("cpuset.sched_relax_domain_level", false).and_then(read_u64_from) + .unwrap_or(0) }, } } /// Control whether the CPUs selected via `set_cpus()` should be exclusive to this control /// group or not. - pub fn set_cpu_exclusive(self: &Self, b: bool) { + pub fn set_cpu_exclusive(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.cpu_exclusive", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Control whether the memory nodes selected via `set_memss()` should be exclusive to this control /// group or not. - pub fn set_mem_exclusive(self: &Self, b: bool) { + pub fn set_mem_exclusive(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.mem_exclusive", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Set the CPUs that the tasks in this control group can run on. /// /// Syntax is a comma separated list of CPUs, with an additional extension that ranges can /// be represented via dashes. - pub fn set_cpus(self: &Self, cpus: &String) { + pub fn set_cpus(self: &Self, cpus: &String) -> Result<(), CgroupError> { self.open_path("cpuset.cpus", true).and_then(|mut file| { - file.write_all(cpus.as_ref()).ok() - }); + file.write_all(cpus.as_ref()).map_err(CgroupError::WriteError) + }) } /// Set the memory nodes that the tasks in this control group can use. /// /// Syntax is the same as with `set_cpus()`. - pub fn set_mems(self: &Self, mems: &String) { + pub fn set_mems(self: &Self, mems: &String) -> Result<(), CgroupError> { self.open_path("cpuset.mems", true).and_then(|mut file| { - file.write_all(mems.as_ref()).ok() - }); + file.write_all(mems.as_ref()).map_err(CgroupError::WriteError) + }) } /// Controls whether the control group should be "hardwalled", i.e., whether kernel allocations @@ -260,71 +245,71 @@ impl CpuSetController { /// /// Note that some kernel allocations, most notably those that are made in interrupt handlers /// may disregard this. - pub fn set_hardwall(self: &Self, b: bool) { + pub fn set_hardwall(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.mem_hardwall", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Controls whether the kernel should attempt to rebalance the load between the CPUs specified in the /// `cpus` field of this control group. - pub fn set_load_balancing(self: &Self, b: bool) { + pub fn set_load_balancing(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.sched_load_balance", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Contorl how much effort the kernel should invest in rebalacing the control group. /// /// See @CpuSet 's similar field for more information. - pub fn set_rebalance_relax_domain_level(self: &Self, i: i64) { + pub fn set_rebalance_relax_domain_level(self: &Self, i: i64) -> Result<(), CgroupError> { self.open_path("cpuset.sched_relax_domain_level", true).and_then(|mut file| { - file.write_all(i.to_string().as_ref()).ok() - }); + file.write_all(i.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Control whether when using `set_mems()` the existing memory used by the tasks should be /// migrated over to the now-selected nodes. - pub fn set_memory_migration(self: &Self, b: bool) { + pub fn set_memory_migration(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.memory_migrate", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Control whether filesystem buffers should be evenly split across the nodes selected via /// `set_mems()`. - pub fn set_memory_spread_page(self: &Self, b: bool) { + pub fn set_memory_spread_page(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.memory_spread_page", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Control whether the kernel's slab cache for file I/O should be evenly split across the /// nodes selected via `set_mems()`. - pub fn set_memory_spread_slab(self: &Self, b: bool) { + pub fn set_memory_spread_slab(self: &Self, b: bool) -> Result<(), CgroupError> { self.open_path("cpuset.memory_spread_slab", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } /// Control whether the kernel should collect information to calculate memory pressure for @@ -332,14 +317,14 @@ impl CpuSetController { /// /// Note: This is a no-operation if the control group referred by `self` is not the root /// control group. - pub fn set_enable_memory_pressure(self: &Self, b: bool) { + pub fn set_enable_memory_pressure(self: &Self, b: bool) -> Result<(), CgroupError> { /* XXX: this file should only be present in the root cpuset cg */ self.open_path("cpuset.memory_pressure_enabled", true).and_then(|mut file| { if b { - file.write_all(b"1").ok() + file.write_all(b"1").map_err(CgroupError::WriteError) } else { - file.write_all(b"0").ok() + file.write_all(b"0").map_err(CgroupError::WriteError) } - }); + }) } } diff --git a/src/devices.rs b/src/devices.rs index ad3e9a0..4ea50a3 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use std::io::{Read, Write}; -use {DeviceResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, DeviceResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; /// A controller that allows controlling the `devices` subsystem of a Cgroup. /// @@ -32,9 +32,9 @@ impl Controller for DevicesController { let wstr = format!("{} {}:{} {}", i.devtype, i.major, i.minor, i.access); if i.allow { - self.allow_device(&wstr); + let _ = self.allow_device(&wstr); } else { - self.deny_device(&wstr); + let _ = self.deny_device(&wstr); } } } @@ -81,10 +81,10 @@ impl DevicesController { /// /// Note that `dev` can be "regex"-like: both `$major` and `$minor` can be `*` which implies /// that their value does not matter. - pub fn allow_device(self: &Self, dev: &String) { + pub fn allow_device(self: &Self, dev: &String) -> Result<(), CgroupError> { self.open_path("devices.allow", true).and_then(|mut file| { - file.write_all(dev.as_ref()).ok() - }); + file.write_all(dev.as_ref()).map_err(CgroupError::WriteError) + }) } /// Deny the control group's tasks access to the devices covered by `dev`. @@ -96,18 +96,21 @@ impl DevicesController { /// /// Note that `dev` can be "regex"-like: both `$major` and `$minor` can be `*` which implies /// that their value does not matter. - pub fn deny_device(self: &Self, dev: &String) { + pub fn deny_device(self: &Self, dev: &String) -> Result<(), CgroupError> { self.open_path("devices.deny", true).and_then(|mut file| { - file.write_all(dev.as_ref()).ok() - }); + file.write_all(dev.as_ref()).map_err(CgroupError::WriteError) + }) } /// Get the current list of allowed devices. - pub fn allowed_devices(self: &Self) -> String { + pub fn allowed_devices(self: &Self) -> Result { self.open_path("devices.list", false).and_then(|mut file| { let mut s = String::new(); - let _ = file.read_to_string(&mut s); - Some(s) - }).unwrap_or("".to_string()) + let res = file.read_to_string(&mut s); + match res { + Ok(_) => Ok(s), + Err(e) => Err(CgroupError::ReadError(e)), + } + }) } } diff --git a/src/freezer.rs b/src/freezer.rs index 1d56fc6..b8d76a8 100644 --- a/src/freezer.rs +++ b/src/freezer.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use std::io::{Read, Write}; -use {Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; /// A controller that allows controlling the `freezer` subsystem of a Cgroup. /// @@ -73,30 +73,33 @@ impl FreezerController { } /// Freezes the processes in the control group. - pub fn freeze(self: &Self) { + pub fn freeze(self: &Self) -> Result<(), CgroupError> { self.open_path("freezer.state", true).and_then(|mut file| { - file.write_all("FROZEN".to_string().as_ref()).ok() - }); + file.write_all("FROZEN".to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Thaws, that is, unfreezes the processes in the control group. - pub fn thaw(self: &Self) { + pub fn thaw(self: &Self) -> Result<(), CgroupError> { self.open_path("freezer.state", true).and_then(|mut file| { - file.write_all("THAWED".to_string().as_ref()).ok() - }); + file.write_all("THAWED".to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Retrieve the state of processes in the control group. - pub fn state(self: &Self) -> FreezerState { + pub fn state(self: &Self) -> Result { self.open_path("freezer.state", false).and_then(|mut file| { let mut s = String::new(); - let _ = file.read_to_string(&mut s); - match s.as_ref() { - "FROZEN" => Some(FreezerState::Frozen), - "THAWED" => Some(FreezerState::Thawed), - "FREEZING" => Some(FreezerState::Freezing), - _ => None, + let res = file.read_to_string(&mut s); + match res { + Ok(_) => match s.as_ref() { + "FROZEN" => Ok(FreezerState::Frozen), + "THAWED" => Ok(FreezerState::Thawed), + "FREEZING" => Ok(FreezerState::Freezing), + _ => Err(CgroupError::ParseError), + }, + Err(e) => Err(CgroupError::ReadError(e)), } - }).unwrap_or(FreezerState::Thawed) + }) } } diff --git a/src/hugetlb.rs b/src/hugetlb.rs index 00b14bf..9c0506a 100644 --- a/src/hugetlb.rs +++ b/src/hugetlb.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::fs::File; use std::io::{Write, Read}; -use {HugePageResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, HugePageResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use CgroupError::*; /// A controller that allows controlling the `hugetlb` subsystem of a Cgroup. @@ -31,7 +32,7 @@ impl Controller for HugeTlbController { if res.update_values { for i in &res.limits { - self.set_limit_in_bytes(&i.size, i.limit); + let _ = self.set_limit_in_bytes(&i.size, i.limit); } } } @@ -57,10 +58,12 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl HugeTlbController { @@ -81,38 +84,38 @@ impl HugeTlbController { } /// Check how many times has the limit of `hugetlb_size` hugepages been hit. - pub fn failcnt(self: &Self, hugetlb_size: &String) -> Option { + pub fn failcnt(self: &Self, hugetlb_size: &String) -> Result { self.open_path(&format!("hugetlb.{}.failcnt", hugetlb_size), false) .and_then(read_u64_from) } /// Get the limit (in bytes) of how much memory can be backed by hugepages of a certain size /// (`hugetlb_size`). - pub fn limit_in_bytes(self: &Self, hugetlb_size: &String) -> Option { + pub fn limit_in_bytes(self: &Self, hugetlb_size: &String) -> Result { self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false) .and_then(read_u64_from) } /// Get the current usage of memory that is backed by hugepages of a certain size /// (`hugetlb_size`). - pub fn usage_in_bytes(self: &Self, hugetlb_size: &String) -> Option { + pub fn usage_in_bytes(self: &Self, hugetlb_size: &String) -> Result { self.open_path(&format!("hugetlb.{}.usage_in_bytes", hugetlb_size), false) .and_then(read_u64_from) } /// Get the maximum observed usage of memory that is backed by hugepages of a certain size /// (`hugetlb_size`). - pub fn max_usage_in_bytes(self: &Self, hugetlb_size: &String) -> Option { + pub fn max_usage_in_bytes(self: &Self, hugetlb_size: &String) -> Result { self.open_path(&format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size), false) .and_then(read_u64_from) } /// Set the limit (in bytes) of how much memory can be backed by hugepages of a certain size /// (`hugetlb_size`). - pub fn set_limit_in_bytes(self: &Self, hugetlb_size: &String, limit: u64) { + pub fn set_limit_in_bytes(self: &Self, hugetlb_size: &String, limit: u64) -> Result<(), CgroupError> { self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false) .and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } } diff --git a/src/lib.rs b/src/lib.rs index 3a4e5fe..e0600b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,29 @@ pub enum Subsystem { 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, +} + #[doc(hidden)] #[derive(Eq, PartialEq, Debug)] pub enum Controllers { @@ -149,32 +172,32 @@ pub trait Controller { } #[doc(hidden)] - fn open_path(self: &Self, p: &str, w: bool) -> Option { + fn open_path(self: &Self, p: &str, w: bool) -> Result { let mut path = self.get_path().clone(); path.push(p); if !self.verify_path() { - return None; + return Err(CgroupError::InvalidPath); } if w { match File::create(&path) { - Err(_) => return None, - Ok(file) => return Some(file), + Err(e) => return Err(CgroupError::WriteError(e)), + Ok(file) => return Ok(file), } } else { match File::open(&path) { - Err(_) => return None, - Ok(file) => return Some(file), + Err(e) => return Err(CgroupError::ReadError(e)), + Ok(file) => return Ok(file), } } } /// Attach a task to this controller. - fn add_task(self: &Self, pid: &CgroupPid) { + fn add_task(self: &Self, pid: &CgroupPid) -> Result<(), CgroupError> { self.open_path("tasks", true).and_then(|mut file| { - file.write_all(pid.pid.to_string().as_ref()).ok() - }); + file.write_all(pid.pid.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Get the list of tasks that this controller has. @@ -188,7 +211,7 @@ pub trait Controller { v.push(n); } } - Some(v.into_iter().map(CgroupPid::from).collect()) + Ok(v.into_iter().map(CgroupPid::from).collect()) }).unwrap_or(vec![]) } } diff --git a/src/memory.rs b/src/memory.rs index 896ae49..a10c03a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::io::{Write, Read}; use std::fs::File; -use {Resources, MemoryResources, Controller, Controllers, Subsystem, ControllIdentifier}; +use {CgroupError, Resources, MemoryResources, Controller, Controllers, Subsystem, ControllIdentifier}; +use CgroupError::*; /// A controller that allows controlling the `memory` subsystem of a Cgroup. /// @@ -125,12 +126,12 @@ impl Controller for MemController { let memres: &MemoryResources = &res.memory; if memres.update_values { - self.set_limit(memres.memory_hard_limit); - self.set_soft_limit(memres.memory_soft_limit); - self.set_kmem_limit(memres.kernel_memory_limit); - self.set_memswap_limit(memres.memory_swap_limit); - self.set_tcp_limit(memres.kernel_tcp_memory_limit); - self.set_swappiness(memres.swappiness); + let _ = self.set_limit(memres.memory_hard_limit); + let _ = self.set_soft_limit(memres.memory_soft_limit); + let _ = self.set_kmem_limit(memres.kernel_memory_limit); + let _ = self.set_memswap_limit(memres.memory_swap_limit); + let _ = self.set_tcp_limit(memres.kernel_tcp_memory_limit); + let _ = self.set_swappiness(memres.swappiness); } } } @@ -154,46 +155,29 @@ impl MemController { pub fn memory_stat(self: &Self) -> Memory { Memory { fail_cnt: self.open_path("memory.failcnt", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), limit_in_bytes: self.open_path("memory.limit_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), usage_in_bytes: self.open_path("memory.usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), max_usage_in_bytes: self.open_path("memory.max_usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), move_charge_at_immigrate: self.open_path("memory.move_charge_at_immigrate", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), numa_stat: self.open_path("memory.numa_stat", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(read_string_from).unwrap_or("".to_string()), oom_control: self.open_path("memory.oom_control", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(read_string_from).unwrap_or("".to_string()), soft_limit_in_bytes: self.open_path("memory.soft_limit_in_bytes", false) - .and_then(|file| read_u64_from(file)) + .and_then(read_u64_from) .unwrap_or(0), stat: self.open_path("memory.stat", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(read_string_from).unwrap_or("".to_string()), swappiness: self.open_path("memory.swappiness", false) - .and_then(|file| read_u64_from(file)) + .and_then(read_u64_from) .unwrap_or(0), use_hierarchy: self.open_path("memory.use_hierarchy", false) - .and_then(|file| read_u64_from(file)) + .and_then(read_u64_from) .unwrap_or(0) } } @@ -202,23 +186,15 @@ impl MemController { pub fn kmem_stat(self: &Self) -> Kmem { Kmem { fail_cnt: self.open_path("memory.kmem.failcnt", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), limit_in_bytes: self.open_path("memory.kmem.limit_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), usage_in_bytes: self.open_path("memory.kmem.usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), max_usage_in_bytes: self.open_path("memory.kmem.max_usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), slabinfo: self.open_path("memory.kmem.slabinfo", false) - .and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) - }).unwrap_or("".to_string()), + .and_then(read_string_from).unwrap_or("".to_string()), } } @@ -227,17 +203,13 @@ impl MemController { pub fn kmem_tcp_stat(self: &Self) -> Tcp { Tcp { fail_cnt: self.open_path("memory.kmem.tcp.failcnt", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), limit_in_bytes: self.open_path("memory.kmem.tcp.limit_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), usage_in_bytes: self.open_path("memory.kmem.tcp.usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), max_usage_in_bytes: self.open_path("memory.kmem.tcp.max_usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), } } @@ -246,46 +218,42 @@ impl MemController { pub fn memswap(self: &Self) -> MemSwap { MemSwap { fail_cnt: self.open_path("memory.memsw.failcnt", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), limit_in_bytes: self.open_path("memory.memsw.limit_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), usage_in_bytes: self.open_path("memory.memsw.usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), max_usage_in_bytes: self.open_path("memory.memsw.max_usage_in_bytes", false) - .and_then(|file| read_u64_from(file)) - .unwrap_or(0), + .and_then(read_u64_from).unwrap_or(0), } } /// Set the memory usage limit of the control group, in bytes. - pub fn set_limit(self: &Self, limit: u64) { + pub fn set_limit(self: &Self, limit: u64) -> Result<(), CgroupError> { self.open_path("memory.limit_in_bytes", true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Set the kernel memory limit of the control group, in bytes. - pub fn set_kmem_limit(self: &Self, limit: u64) { + pub fn set_kmem_limit(self: &Self, limit: u64) -> Result<(), CgroupError> { self.open_path("memory.kmem.limit_in_bytes", true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Set the memory+swap limit of the control group, in bytes. - pub fn set_memswap_limit(self: &Self, limit: u64) { + pub fn set_memswap_limit(self: &Self, limit: u64) -> Result<(), CgroupError> { self.open_path("memory.memsw.limit_in_bytes", true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } /// Set how much kernel memory can be used for TCP-related buffers by the control group. - pub fn set_tcp_limit(self: &Self, limit: u64) { + pub fn set_tcp_limit(self: &Self, limit: u64) -> Result<(), CgroupError> { self.open_path("memory.kmem.tcp.limit_in_bytes", true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } @@ -293,10 +261,10 @@ impl MemController { /// /// This limit is enforced when the system is nearing OOM conditions. Contrast this with the /// hard limit, which is _always_ enforced. - pub fn set_soft_limit(self: &Self, limit: u64) { + pub fn set_soft_limit(self: &Self, limit: u64) -> Result<(), CgroupError> { self.open_path("memory.soft_limit_in_bytes", true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()).ok() - }); + file.write_all(limit.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } @@ -304,10 +272,10 @@ impl MemController { /// group. /// /// Note that a value of zero does not imply that the process will not be swapped out. - pub fn set_swappiness(self: &Self, swp: u64) { + pub fn set_swappiness(self: &Self, swp: u64) -> Result<(), CgroupError> { self.open_path("memory.swappiness", true).and_then(|mut file| { - file.write_all(swp.to_string().as_ref()).ok() - }); + file.write_all(swp.to_string().as_ref()).map_err(CgroupError::WriteError) + }) } } @@ -331,8 +299,18 @@ impl<'a> From<&'a Subsystem> for &'a MemController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } +} + +fn read_string_from(mut file: File) -> Result { + let mut string = String::new(); + match file.read_to_string(&mut string) { + Ok(_) => Ok(string.trim().to_string()), + Err(e) => Err(CgroupError::ReadError(e)), + } } diff --git a/src/net_cls.rs b/src/net_cls.rs index 61ab7d2..febe901 100644 --- a/src/net_cls.rs +++ b/src/net_cls.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::io::{Read, Write}; use std::fs::File; -use {NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use CgroupError::*; /// A controller that allows controlling the `net_cls` subsystem of a Cgroup. /// @@ -30,7 +31,7 @@ impl Controller for NetClsController { let res: &NetworkResources = &res.network; if res.update_values { - self.set_class(res.class_id); + let _ = self.set_class(res.class_id); } } } @@ -55,10 +56,12 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl NetClsController { @@ -73,17 +76,17 @@ impl NetClsController { } /// Set the network class id of the outgoing packets of the control group's tasks. - pub fn set_class(self: &Self, class: u64) { + pub fn set_class(self: &Self, class: u64) -> Result<(), CgroupError> { self.open_path("net_cls.classid", true).and_then(|mut file| { let s = format!("{:#08X}", class); - file.write_all(s.as_ref()).ok() - }); + file.write_all(s.as_ref()).map_err(CgroupError::WriteError) + }) } /// Get the network class id of the outgoing packets of the control group's tasks. - pub fn get_class(self: &Self) -> u64 { + pub fn get_class(self: &Self) -> Result { self.open_path("net_cls.classid", false).and_then(|file| { read_u64_from(file) - }).unwrap_or(0u64) + }) } } diff --git a/src/net_prio.rs b/src/net_prio.rs index bc7c2b9..10082a2 100644 --- a/src/net_prio.rs +++ b/src/net_prio.rs @@ -7,7 +7,8 @@ use std::io::{BufReader, BufRead, Write, Read}; use std::fs::File; use std::collections::HashMap; -use {NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, NetworkResources, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use CgroupError::*; /// A controller that allows controlling the `net_prio` subsystem of a Cgroup. /// @@ -32,7 +33,7 @@ impl Controller for NetPrioController { if res.update_values { for i in &res.priorities { - self.set_if_prio(&i.name, i.priority); + let _ = self.set_if_prio(&i.name, i.priority); } } } @@ -58,10 +59,12 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController { } } -fn read_u64_from(mut file: File) -> Option { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - string.trim().parse().ok() + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl NetPrioController { @@ -83,24 +86,39 @@ impl NetPrioController { } /// A map of priorities for each network interface. - pub fn ifpriomap(self: &Self) -> HashMap { - self.open_path("net_prio.ifpriomap", false) - .and_then(|file| { - let bf = BufReader::new(file); - Some(bf.lines().map(|line| { + pub fn ifpriomap(self: &Self) -> Result, CgroupError> { + self.open_path("net_prio.ifpriomap", false) .and_then(|file| { + let bf = BufReader::new(file); + bf.lines().fold(Ok(HashMap::new()), |acc, line| { + if acc.is_err() { + acc + } else { + let mut acc = acc.unwrap(); let l = line.unwrap(); let mut sp = l.split_whitespace(); - (sp.nth(0).unwrap().to_string(), - sp.nth(1).unwrap().trim().parse().unwrap()) - }).collect()) - }).unwrap_or(HashMap::new()) + let ifname = sp.nth(0); + let ifprio = sp.nth(1); + if ifname.is_none() || ifprio.is_none() { + Err(CgroupError::ParseError) + } else { + let ifname = ifname.unwrap(); + let ifprio = ifprio.unwrap().trim().parse(); + if ifprio.is_err() { + Err(CgroupError::ParseError) + } else { + acc.insert(ifname.to_string(), ifprio.unwrap()); + Ok(acc) + } + } + } + }) + }) } /// Set the priority of the network traffic on `eif` to be `prio`. - pub fn set_if_prio(self: &Self, eif: &String, prio: u64) { - self.open_path("net_prio.ifpriomap", true) - .and_then(|mut file| { - Some(file.write_all(format!("{} {}", eif, prio).as_ref())) - }); + pub fn set_if_prio(self: &Self, eif: &String, prio: u64) -> Result<(), CgroupError> { + self.open_path("net_prio.ifpriomap", true).and_then(|mut file| { + file.write_all(format!("{} {}", eif, prio).as_ref()).map_err(CgroupError::WriteError) + }) } } diff --git a/src/pid.rs b/src/pid.rs index 8e279a5..32bc1b2 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -4,8 +4,10 @@ //! [Documentation/cgroups-v1/pids.txt](https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt) use std::path::PathBuf; use std::io::{Write, Read}; +use std::fs::File; -use {Resources, PidResources, Controller, ControllIdentifier, Subsystem, Controllers}; +use {CgroupError, Resources, PidResources, Controller, ControllIdentifier, Subsystem, Controllers}; +use CgroupError::*; /// A controller that allows controlling the `pids` subsystem of a Cgroup. #[derive(Debug, Clone)] @@ -42,7 +44,7 @@ impl Controller for PidController { if pidres.update_values { /* apply pid_max */ - self.set_pid_max(pidres.maximum_number_of_processes); + let _ = self.set_pid_max(pidres.maximum_number_of_processes); } } } @@ -73,6 +75,14 @@ impl<'a> From<&'a Subsystem> for &'a PidController { } } +fn read_u64_from(mut file: File) -> Result { + let mut string = String::new(); + match file.read_to_string(&mut string) { + Ok(_) => string.trim().parse().map_err(|_| ParseError), + Err(e) => Err(CgroupError::ReadError(e)), + } +} + impl PidController { /// Constructors a new `PidController` instance, with `oroot` serving as the controller's root /// directory. @@ -86,32 +96,44 @@ impl PidController { } /// The number of times `fork` failed because the limit was hit. - pub fn get_pid_events(self: &Self) -> i64 { + pub fn get_pid_events(self: &Self) -> Result { self.open_path("pids.events", false).and_then(|mut file| { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.split_whitespace().nth(1).unwrap().parse().unwrap_or(0)) - }).unwrap() + match file.read_to_string(&mut string) { + Ok(_) => { + match string.split_whitespace().nth(1) { + Some(elem) => match elem.parse() { + Ok(val) => Ok(val), + Err(_) => Err(CgroupError::ParseError), + }, + None => Err(CgroupError::ParseError), + } + }, + Err(e) => Err(CgroupError::ReadError(e)), + } + }) } /// The number of processes currently. - pub fn get_pid_current(self: &Self) -> i64 { - self.open_path("pids.current", false).and_then(|mut file| { - let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().parse().unwrap_or(0)) - }).unwrap() + pub fn get_pid_current(self: &Self) -> Result { + self.open_path("pids.current", false).and_then(read_u64_from) } /// The maximum number of processes that can exist at one time in the control group. - pub fn get_pid_max(self: &Self) -> Option { + pub fn get_pid_max(self: &Self) -> Result { self.open_path("pids.max", false).and_then(|mut file| { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - if string.trim() == "max" { - Some(PidMax::Max) - } else { - Some(PidMax::Value(string.trim().parse().unwrap_or(0))) + let res = file.read_to_string(&mut string); + match res { + Ok(_) => if string.trim() == "max" { + Ok(PidMax::Max) + } else { + match string.trim().parse() { + Ok(val) => Ok(PidMax::Value(val)), + Err(_) => Err(CgroupError::ParseError), + } + }, + Err(e) => Err(CgroupError::ReadError(e)), } }) } @@ -121,17 +143,16 @@ impl PidController { /// Note that if `get_pid_current()` returns a higher number than what you /// are about to set (`max_pid`), then no processess will be killed. Additonally, attaching /// extra processes to a control group disregards the limit. - pub fn set_pid_max(self: &Self, max_pid: PidMax) { + pub fn set_pid_max(self: &Self, max_pid: PidMax) -> Result<(), CgroupError> { self.open_path("pids.max", true).and_then(|mut file| { let string_to_write = match max_pid { PidMax::Max => "max".to_string(), PidMax::Value(num) => num.to_string(), }; match file.write_all(string_to_write.as_ref()) { - Ok(_) => (), - Err(e) => println!("error {:?}", e), + Ok(_) => Ok(()), + Err(e) => Err(CgroupError::WriteError(e)), } - Some(0i64) - }); + }) } } diff --git a/src/rdma.rs b/src/rdma.rs index e326c8a..82fea7b 100644 --- a/src/rdma.rs +++ b/src/rdma.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; use std::io::{Write, Read}; use std::fs::File; -use {Controllers, Controller, Resources, ControllIdentifier, Subsystem}; +use {CgroupError, Controllers, Controller, Resources, ControllIdentifier, Subsystem}; /// A controller that allows controlling the `rdma` subsystem of a Cgroup. /// @@ -48,10 +48,12 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController { } } -fn read_string_from(mut file: File) -> Option { +fn read_string_from(mut file: File) -> Result { let mut string = String::new(); - let _ = file.read_to_string(&mut string); - Some(string.trim().to_string()) + match file.read_to_string(&mut string) { + Ok(_) => Ok(string.trim().to_string()), + Err(e) => Err(CgroupError::ReadError(e)), + } } impl RdmaController { @@ -66,17 +68,15 @@ impl RdmaController { } /// Returns the current usage of RDMA/IB specific resources. - pub fn current(self: &Self) -> String { + pub fn current(self: &Self) -> Result { self.open_path("rdma.current", false) .and_then(read_string_from) - .unwrap_or("".to_string()) } /// Set a maximum usage for each RDMA/IB resource. - pub fn set_max(self: &Self, max: &String) { - self.open_path("rdma.max", true) - .and_then(|mut file| { - file.write_all(max.as_ref()).ok() - }); + pub fn set_max(self: &Self, max: &String) -> Result<(), CgroupError> { + self.open_path("rdma.max", true).and_then(|mut file| { + file.write_all(max.as_ref()).map_err(CgroupError::WriteError) + }) } } diff --git a/tests/pids.rs b/tests/pids.rs index f1e32f9..bc70869 100644 --- a/tests/pids.rs +++ b/tests/pids.rs @@ -1,6 +1,6 @@ //! Integration tests about the pids subsystem extern crate cgroups; -use cgroups::{CgroupPid, Cgroup, Resources, PidResources}; +use cgroups::{CgroupError, CgroupPid, Cgroup, Resources, PidResources}; use cgroups::pid::{PidController, PidMax}; use cgroups::Controller;