From 7826b798bdbb638b5067bbab426a42c5645edbf0 Mon Sep 17 00:00:00 2001 From: Sam Wilson Date: Wed, 10 Oct 2018 14:03:28 -0400 Subject: [PATCH] Implement ::std::error::Error --- src/blkio.rs | 97 +++++++++++++++++++++++++---------------------- src/cgroup.rs | 8 ++-- src/cpu.rs | 45 ++++++++++++---------- src/cpuacct.rs | 21 +++++----- src/cpuset.rs | 88 +++++++++++++++++++++--------------------- src/devices.rs | 23 ++++++----- src/error.rs | 87 ++++++++++++++++++++++++++++++++++++++++++ src/freezer.rs | 21 +++++----- src/hugetlb.rs | 30 ++++++++------- src/lib.rs | 70 ++++++++++------------------------ src/memory.rs | 69 +++++++++++++++++---------------- src/net_cls.rs | 24 ++++++------ src/net_prio.rs | 33 ++++++++-------- src/perf_event.rs | 6 ++- src/pid.rs | 38 ++++++++++--------- src/rdma.rs | 17 +++++---- tests/cpuset.rs | 6 +-- tests/pids.rs | 2 +- 18 files changed, 392 insertions(+), 293 deletions(-) create mode 100644 src/error.rs diff --git a/src/blkio.rs b/src/blkio.rs index 77dad2c..1d415a5 100644 --- a/src/blkio.rs +++ b/src/blkio.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - BlkIoResources, CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem, + BlkIoResources, ControllIdentifier, Controller, Controllers, Resources, Subsystem, }; /// A controller that allows controlling the `blkio` subsystem of a Cgroup. @@ -51,7 +53,7 @@ pub struct IoService { pub total: u64, } -fn parse_io_service(s: String) -> Result, CgroupError> { +fn parse_io_service(s: String) -> Result> { s.lines() .filter(|x| x.split_whitespace().collect::>().len() == 3) .map(|x| { @@ -83,7 +85,7 @@ fn parse_io_service(s: String) -> Result, CgroupError> { }) .fold(Ok(Vec::new()), |acc, x| { if acc.is_err() || x.is_none() { - Err(CgroupError::ParseError) + Err(Error::new(ParseError)) } else { let mut acc = acc.unwrap(); acc.push(x.unwrap()); @@ -92,18 +94,18 @@ fn parse_io_service(s: String) -> Result, CgroupError> { }) } -fn parse_io_service_total(s: String) -> Result { +fn parse_io_service_total(s: String) -> Result { s.lines() .filter(|x| x.split_whitespace().collect::>().len() == 2) - .fold(Err(CgroupError::ParseError), |_, x| { + .fold(Err(Error::new(ParseError)), |_, x| { match x.split_whitespace().collect::>().as_slice() { - ["Total", val] => val.parse::().map_err(|_| CgroupError::ParseError), - _ => Err(CgroupError::ParseError), + ["Total", val] => val.parse::().map_err(|_| Error::new(ParseError)), + _ => Err(Error::new(ParseError)), } }) } -fn parse_blkio_data(s: String) -> Result, CgroupError> { +fn parse_blkio_data(s: String) -> Result> { let r = s .chars() .map(|x| if x == ':' { ' ' } else { x }) @@ -127,11 +129,11 @@ fn parse_blkio_data(s: String) -> Result, CgroupError> { }); Ok(()) } - _ => Err(CgroupError::ParseError), + _ => Err(Error::new(ParseError)), }); if err.is_err() { - return Err(CgroupError::ParseError); + return Err(Error::new(ParseError)); } else { return Ok(res); } @@ -267,7 +269,7 @@ impl Controller for BlkIoController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &BlkIoResources = &res.blkio; @@ -320,19 +322,19 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController { } } -fn read_string_from(mut file: File) -> Result { +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)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -569,26 +571,26 @@ 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, w: u64) -> Result<(), CgroupError> { + pub fn set_leaf_weight(&self, w: u64) -> Result<()> { self.open_path("blkio.leaf_weight", true) .and_then(|mut file| { file.write_all(w.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Same as `set_leaf_weight()`, but settable per each block device. - pub fn set_leaf_weight_for_device(&self, d: String) -> Result<(), CgroupError> { + pub fn set_leaf_weight_for_device(&self, d: String) -> Result<()> { self.open_path("blkio.leaf_weight_device", true) - .and_then(|mut file| file.write_all(d.as_ref()).map_err(CgroupError::WriteError)) + .and_then(|mut file| file.write_all(d.as_ref()).map_err(|e| Error::with_cause(WriteFailed, e))) } /// Reset the statistics the kernel has gathered so far and start fresh. - pub fn reset_stats(&self) -> Result<(), CgroupError> { + pub fn reset_stats(&self) -> Result<()> { self.open_path("blkio.leaf_weight_device", true) .and_then(|mut file| { file.write_all("1".to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -599,11 +601,11 @@ impl BlkIoController { major: u64, minor: u64, bps: u64, - ) -> Result<(), CgroupError> { + ) -> Result<()> { self.open_path("blkio.throttle.read_bps_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -614,11 +616,11 @@ impl BlkIoController { major: u64, minor: u64, iops: u64, - ) -> Result<(), CgroupError> { + ) -> Result<()> { self.open_path("blkio.throttle.read_iops_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Throttle the bytes per second rate of write operation affecting the block device @@ -628,11 +630,11 @@ impl BlkIoController { major: u64, minor: u64, bps: u64, - ) -> Result<(), CgroupError> { + ) -> Result<()> { self.open_path("blkio.throttle.write_bps_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, bps).to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -643,20 +645,20 @@ impl BlkIoController { major: u64, minor: u64, iops: u64, - ) -> Result<(), CgroupError> { + ) -> Result<()> { self.open_path("blkio.throttle.write_iops_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, iops).to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Set the weight of the control group's tasks. - pub fn set_weight(&self, w: u64) -> Result<(), CgroupError> { + pub fn set_weight(&self, w: u64) -> Result<()> { self.open_path("blkio.leaf_weight", true) .and_then(|mut file| { file.write_all(w.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -666,11 +668,11 @@ impl BlkIoController { major: u64, minor: u64, weight: u64, - ) -> Result<(), CgroupError> { + ) -> Result<()> { self.open_path("blkio.weight_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, weight).as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } } @@ -679,7 +681,7 @@ impl BlkIoController { mod test { use blkio::{parse_blkio_data, BlkIoData}; use blkio::{parse_io_service, parse_io_service_total, IoService}; - use CgroupError; + use error::*; static TEST_VALUE: &str = "\ 8:32 Read 4280320 @@ -736,17 +738,19 @@ Total 61823067136 #[test] fn test_parse_io_service_total() { + let ok = parse_io_service_total(TEST_VALUE.to_string()).unwrap(); assert_eq!( - parse_io_service_total(TEST_VALUE.to_string()), - Ok(61823067136) + ok, + 61823067136 ); } #[test] fn test_parse_io_service() { + let ok = parse_io_service(TEST_VALUE.to_string()).unwrap(); assert_eq!( - parse_io_service(TEST_VALUE.to_string()), - Ok(vec![ + ok, + vec![ IoService { major: 8, minor: 32, @@ -783,19 +787,20 @@ Total 61823067136 async: 0, total: 7192576, } - ]) + ] ); + let err = parse_io_service(TEST_WRONG_VALUE.to_string()).unwrap_err(); assert_eq!( - parse_io_service(TEST_WRONG_VALUE.to_string()), - Err(CgroupError::ParseError) + err.kind(), + &ErrorKind::ParseError, ); } #[test] fn test_parse_blkio_data() { assert_eq!( - parse_blkio_data(TEST_BLKIO_DATA.to_string()), - Ok(vec![ + parse_blkio_data(TEST_BLKIO_DATA.to_string()).unwrap(), + vec![ BlkIoData { major: 8, minor: 48, @@ -816,7 +821,7 @@ Total 61823067136 minor: 0, data: 559583764, } - ]) + ] ); } } diff --git a/src/cgroup.rs b/src/cgroup.rs index e3a10b2..020fca1 100644 --- a/src/cgroup.rs +++ b/src/cgroup.rs @@ -1,6 +1,8 @@ //! This module handles cgroup operations. Start here! -use {CgroupError, CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem}; +use error::*; + +use {CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem}; use std::convert::From; @@ -98,7 +100,7 @@ impl<'b> Cgroup<'b> { } /// Apply a set of resource limits to the control group. - pub fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + pub fn apply(&self, res: &Resources) -> Result<()> { self.subsystems .iter() .try_fold((), |_, e| e.to_controller().apply(res)) @@ -138,7 +140,7 @@ impl<'b> Cgroup<'b> { } /// Attach a task to the control group. - pub fn add_task(&self, pid: CgroupPid) -> Result<(), CgroupError> { + pub fn add_task(&self, pid: CgroupPid) -> Result<()> { self.subsystems() .iter() .try_for_each(|sub| sub.to_controller().add_task(&pid)) diff --git a/src/cpu.rs b/src/cpu.rs index 38825bf..7d5e675 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -7,8 +7,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem, + ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem, }; /// A controller that allows controlling the `cpu` subsystem of a Cgroup. @@ -48,25 +51,25 @@ impl Controller for CpuController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &CpuResources = &res.cpu; if res.update_values { // apply pid_max let _ = self.set_shares(res.shares); - if self.shares() != Ok(res.shares as u64) { - return Err(CgroupError::Unknown); + if self.shares()? != res.shares as u64 { + return Err(Error::new(ErrorKind::Other)); } let _ = self.set_cfs_period(res.period); - if self.cfs_period() != Ok(res.period as u64) { - return Err(CgroupError::Unknown); + if self.cfs_period()? != res.period as u64 { + return Err(Error::new(ErrorKind::Other)); } let _ = self.set_cfs_quota(res.quota as u64); - if self.cfs_quota() != Ok(res.quota as u64) { - return Err(CgroupError::Unknown); + if self.cfs_quota()? != res.quota as u64 { + return Err(Error::new(ErrorKind::Other)); } // TODO: rt properties (CONFIG_RT_GROUP_SCHED) are not yet supported @@ -96,11 +99,11 @@ impl<'a> From<&'a Subsystem> for &'a CpuController { } } -fn read_u64_from(mut file: File) -> Result { +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(|_| CgroupError::ParseError), - Err(e) => Err(CgroupError::ReadError(e)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -125,7 +128,7 @@ impl CpuController { let res = file.read_to_string(&mut s); match res { Ok(_) => Ok(s), - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } }).unwrap_or("".to_string()), } @@ -137,49 +140,49 @@ 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, shares: u64) -> Result<(), CgroupError> { + pub fn set_shares(&self, shares: u64) -> Result<()> { self.open_path("cpu.shares", true).and_then(|mut file| { file.write_all(shares.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Retrieve the CPU bandwidth that this control group (relative to other control groups and /// this control group's parent) can use. - pub fn shares(&self) -> Result { + pub fn shares(&self) -> Result { self.open_path("cpu.shares", false).and_then(read_u64_from) } /// 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, us: u64) -> Result<(), CgroupError> { + pub fn set_cfs_period(&self, us: u64) -> Result<()> { self.open_path("cpu.cfs_period_us", true) .and_then(|mut file| { file.write_all(us.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Retrieve the period of time of how often this cgroup's access to the CPU should be /// reallocated in microseconds. - pub fn cfs_period(&self) -> Result { + pub fn cfs_period(&self) -> Result { self.open_path("cpu.cfs_period_us", false) .and_then(read_u64_from) } /// 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, us: u64) -> Result<(), CgroupError> { + pub fn set_cfs_quota(&self, us: u64) -> Result<()> { self.open_path("cpu.cfs_quota_us", true) .and_then(|mut file| { file.write_all(us.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Retrieve the quota of time for which all tasks in this cgroup can run during one period, in /// microseconds. - pub fn cfs_quota(&self) -> Result { + pub fn cfs_quota(&self) -> Result { self.open_path("cpu.cfs_quota_us", false) .and_then(read_u64_from) } diff --git a/src/cpuacct.rs b/src/cpuacct.rs index b379bcb..0dffce5 100644 --- a/src/cpuacct.rs +++ b/src/cpuacct.rs @@ -6,7 +6,10 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem}; +use error::*; +use error::ErrorKind::*; + +use {ControllIdentifier, Controller, Controllers, Resources, Subsystem}; /// A controller that allows controlling the `cpuacct` subsystem of a Cgroup. /// @@ -63,7 +66,7 @@ impl Controller for CpuAcctController { &self.base } - fn apply(&self, _res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, _res: &Resources) -> Result<()> { Ok(()) } } @@ -88,23 +91,23 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController { } } -fn read_u64_from(mut file: File) -> Result { +fn read_u64_from(mut file: File) -> Result { let mut string = String::new(); 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(Error::with_cause(ParseError, e)), }, - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } -fn read_string_from(mut file: File) -> Result { +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)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -158,8 +161,8 @@ impl CpuAcctController { } /// Reset the statistics the kernel has gathered about the control group. - pub fn reset(&self) -> Result<(), CgroupError> { + pub fn reset(&self) -> Result<()> { self.open_path("cpuacct.usage", true) - .and_then(|mut file| file.write_all(b"0").map_err(CgroupError::WriteError)) + .and_then(|mut file| file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e))) } } diff --git a/src/cpuset.rs b/src/cpuset.rs index 786e0aa..2ef4ba2 100644 --- a/src/cpuset.rs +++ b/src/cpuset.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem, + ControllIdentifier, Controller, Controllers, CpuResources, Resources, Subsystem, }; /// A controller that allows controlling the `cpuset` subsystem of a Cgroup. @@ -91,7 +93,7 @@ impl Controller for CpuSetController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &CpuResources = &res.cpu; @@ -124,24 +126,24 @@ impl<'a> From<&'a Subsystem> for &'a CpuSetController { } } -fn read_string_from(mut file: File) -> Result { +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)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } /// Parse a string like "1,2,4-5,8" into a list of (start, end) tuples. -fn parse_range(s: String) -> Result, CgroupError> { +fn parse_range(s: String) -> Result> { let mut fin = Vec::new(); if s == "".to_string() { @@ -156,19 +158,19 @@ fn parse_range(s: String) -> Result, CgroupError> { // this is a true range let dash_split = sp.split("-").collect::>(); if dash_split.len() != 2 { - return Err(CgroupError::ParseError); + return Err(Error::new(ParseError)); } let first = dash_split[0].parse::(); let second = dash_split[1].parse::(); if first.is_err() || second.is_err() { - return Err(CgroupError::ParseError); + return Err(Error::new(ParseError)); } fin.push((first.unwrap(), second.unwrap())); } else { // this is just a single number let num = sp.parse::(); if num.is_err() { - return Err(CgroupError::ParseError); + return Err(Error::new(ParseError)); } fin.push((num.clone().unwrap(), num.clone().unwrap())); } @@ -279,26 +281,26 @@ impl CpuSetController { /// Control whether the CPUs selected via `set_cpus()` should be exclusive to this control /// group or not. - pub fn set_cpu_exclusive(&self, b: bool) -> Result<(), CgroupError> { + pub fn set_cpu_exclusive(&self, b: bool) -> Result<()> { self.open_path("cpuset.cpu_exclusive", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } /// Control whether the memory nodes selected via `set_memss()` should be exclusive to this control /// group or not. - pub fn set_mem_exclusive(&self, b: bool) -> Result<(), CgroupError> { + pub fn set_mem_exclusive(&self, b: bool) -> Result<()> { self.open_path("cpuset.mem_exclusive", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } @@ -307,20 +309,20 @@ impl CpuSetController { /// /// Syntax is a comma separated list of CPUs, with an additional extension that ranges can /// be represented via dashes. - pub fn set_cpus(&self, cpus: &String) -> Result<(), CgroupError> { + pub fn set_cpus(&self, cpus: &String) -> Result<()> { self.open_path("cpuset.cpus", true).and_then(|mut file| { file.write_all(cpus.as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// 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, mems: &String) -> Result<(), CgroupError> { + pub fn set_mems(&self, mems: &String) -> Result<()> { self.open_path("cpuset.mems", true).and_then(|mut file| { file.write_all(mems.as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -329,26 +331,26 @@ impl CpuSetController { /// /// Note that some kernel allocations, most notably those that are made in interrupt handlers /// may disregard this. - pub fn set_hardwall(&self, b: bool) -> Result<(), CgroupError> { + pub fn set_hardwall(&self, b: bool) -> Result<()> { self.open_path("cpuset.mem_hardwall", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } /// 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, b: bool) -> Result<(), CgroupError> { + pub fn set_load_balancing(&self, b: bool) -> Result<()> { self.open_path("cpuset.sched_load_balance", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } @@ -356,49 +358,49 @@ impl CpuSetController { /// 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, i: i64) -> Result<(), CgroupError> { + pub fn set_rebalance_relax_domain_level(&self, i: i64) -> Result<()> { self.open_path("cpuset.sched_relax_domain_level", true) .and_then(|mut file| { file.write_all(i.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// 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, b: bool) -> Result<(), CgroupError> { + pub fn set_memory_migration(&self, b: bool) -> Result<()> { self.open_path("cpuset.memory_migrate", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } /// Control whether filesystem buffers should be evenly split across the nodes selected via /// `set_mems()`. - pub fn set_memory_spread_page(&self, b: bool) -> Result<(), CgroupError> { + pub fn set_memory_spread_page(&self, b: bool) -> Result<()> { self.open_path("cpuset.memory_spread_page", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } /// 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, b: bool) -> Result<(), CgroupError> { + pub fn set_memory_spread_slab(&self, b: bool) -> Result<()> { self.open_path("cpuset.memory_spread_slab", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } @@ -408,16 +410,16 @@ impl CpuSetController { /// /// Note: This will fail with `InvalidOperation` if the current congrol group is not the root /// control group. - pub fn set_enable_memory_pressure(&self, b: bool) -> Result<(), CgroupError> { + pub fn set_enable_memory_pressure(&self, b: bool) -> Result<()> { if !self.path_exists("cpuset.memory_pressure_enabled") { - return Err(CgroupError::InvalidOperation); + return Err(Error::new(InvalidOperation)); } self.open_path("cpuset.memory_pressure_enabled", true) .and_then(|mut file| { if b { - file.write_all(b"1").map_err(CgroupError::WriteError) + file.write_all(b"1").map_err(|e| Error::with_cause(WriteFailed, e)) } else { - file.write_all(b"0").map_err(CgroupError::WriteError) + file.write_all(b"0").map_err(|e| Error::with_cause(WriteFailed, e)) } }) } diff --git a/src/devices.rs b/src/devices.rs index 195f7b4..4781e53 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -5,8 +5,11 @@ use std::io::{Read, Write}; use std::path::PathBuf; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, DeviceResource, DeviceResources, + ControllIdentifier, Controller, Controllers, DeviceResource, DeviceResources, Resources, Subsystem, }; @@ -142,7 +145,7 @@ impl Controller for DevicesController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &DeviceResources = &res.devices; @@ -201,7 +204,7 @@ impl DevicesController { major: i64, minor: i64, perm: &Vec, - ) -> Result<(), CgroupError> { + ) -> Result<()> { let perms = perm .iter() .map(DevicePermissions::to_char) @@ -219,7 +222,7 @@ impl DevicesController { let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); self.open_path("devices.allow", true).and_then(|mut file| { file.write_all(final_str.as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -233,7 +236,7 @@ impl DevicesController { major: i64, minor: i64, perm: &Vec, - ) -> Result<(), CgroupError> { + ) -> Result<()> { let perms = perm .iter() .map(DevicePermissions::to_char) @@ -251,12 +254,12 @@ impl DevicesController { let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); self.open_path("devices.deny", true).and_then(|mut file| { file.write_all(final_str.as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Get the current list of allowed devices. - pub fn allowed_devices(&self) -> Result, CgroupError> { + pub fn allowed_devices(&self) -> Result> { self.open_path("devices.list", false).and_then(|mut file| { let mut s = String::new(); let res = file.read_to_string(&mut s); @@ -266,7 +269,7 @@ impl DevicesController { let ls = line.to_string().split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::>(); if acc.is_err() || ls.len() != 4 { error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls); - Err(CgroupError::ParseError) + Err(Error::new(ParseError)) } else { let devtype = DeviceType::from_char(ls[0].chars().nth(0)); let mut major = ls[1].parse::(); @@ -280,7 +283,7 @@ impl DevicesController { if devtype.is_none() || major.is_err() || minor.is_err() || !DevicePermissions::is_valid(&ls[3]) { error!("allowed_devices: acc: {:?}, ls: {:?}, devtype: {:?}, major {:?} minor {:?} ls3 {:?}", acc, ls, devtype, major, minor, &ls[3]); - Err(CgroupError::ParseError) + Err(Error::new(ParseError)) } else { let access = DevicePermissions::from_string(&ls[3]); let mut acc = acc.unwrap(); @@ -296,7 +299,7 @@ impl DevicesController { } }) }, - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } }) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..70eb4ec --- /dev/null +++ b/src/error.rs @@ -0,0 +1,87 @@ +use std::error::Error as StdError; +use std::fmt; + +/// The different types of errors that can occur while manipulating control groups. +#[derive(Debug, Eq, PartialEq)] +pub enum ErrorKind { + /// An error occured while writing to a control group file. + WriteFailed, + + /// An error occured while trying to read from a control group file. + ReadFailed, + + /// 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. + Other, +} + +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, + cause: Option>, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match self.kind { + ErrorKind::WriteFailed => "unable to write to a control group file", + ErrorKind::ReadFailed => "unable to read a control group file", + ErrorKind::ParseError => "unable to parse control group file", + ErrorKind::InvalidOperation => "the requested operation is invalid", + ErrorKind::InvalidPath => "the given path is invalid", + ErrorKind::Other => "an unknown error", + }; + + write!(f, "{}", msg) + } +} + +impl StdError for Error { + fn cause(&self) -> Option<&StdError> { + match self.cause { + Some(ref x) => Some(&**x), + None => None, + } + } +} + +impl Error { + pub(crate) fn new(kind: ErrorKind) -> Self { + Self { + kind, + cause: None, + } + } + + pub(crate) fn with_cause(kind: ErrorKind, cause: E) -> Self + where + E: 'static + Send + StdError, + { + Self { + kind, + cause: Some(Box::new(cause)), + } + } + + pub fn kind(&self) -> &ErrorKind { + &self.kind + } +} + +pub type Result = ::std::result::Result; diff --git a/src/freezer.rs b/src/freezer.rs index 3e4556a..a59e187 100644 --- a/src/freezer.rs +++ b/src/freezer.rs @@ -5,7 +5,10 @@ use std::io::{Read, Write}; use std::path::PathBuf; -use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem}; +use error::*; +use error::ErrorKind::*; + +use {ControllIdentifier, Controller, Controllers, Resources, Subsystem}; /// A controller that allows controlling the `freezer` subsystem of a Cgroup. /// @@ -45,7 +48,7 @@ impl Controller for FreezerController { &self.base } - fn apply(&self, _res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, _res: &Resources) -> Result<()> { Ok(()) } } @@ -82,23 +85,23 @@ impl FreezerController { } /// Freezes the processes in the control group. - pub fn freeze(&self) -> Result<(), CgroupError> { + pub fn freeze(&self) -> Result<()> { self.open_path("freezer.state", true).and_then(|mut file| { file.write_all("FROZEN".to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Thaws, that is, unfreezes the processes in the control group. - pub fn thaw(&self) -> Result<(), CgroupError> { + pub fn thaw(&self) -> Result<()> { self.open_path("freezer.state", true).and_then(|mut file| { file.write_all("THAWED".to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Retrieve the state of processes in the control group. - pub fn state(&self) -> Result { + pub fn state(&self) -> Result { self.open_path("freezer.state", false).and_then(|mut file| { let mut s = String::new(); let res = file.read_to_string(&mut s); @@ -107,9 +110,9 @@ impl FreezerController { "FROZEN" => Ok(FreezerState::Frozen), "THAWED" => Ok(FreezerState::Thawed), "FREEZING" => Ok(FreezerState::Freezing), - _ => Err(CgroupError::ParseError), + _ => Err(Error::new(ParseError)), }, - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } }) } diff --git a/src/hugetlb.rs b/src/hugetlb.rs index 64e9e39..9965d42 100644 --- a/src/hugetlb.rs +++ b/src/hugetlb.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, HugePageResources, Resources, + ControllIdentifier, Controller, Controllers, HugePageResources, Resources, Subsystem, }; @@ -36,15 +38,15 @@ impl Controller for HugeTlbController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &HugePageResources = &res.hugepages; if res.update_values { for i in &res.limits { let _ = self.set_limit_in_bytes(&i.size, i.limit); - if self.limit_in_bytes(&i.size) != Ok(i.limit) { - return Err(CgroupError::Unknown); + if self.limit_in_bytes(&i.size)? != i.limit { + return Err(Error::new(Other)); } } } @@ -72,11 +74,11 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController { } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -98,28 +100,28 @@ impl HugeTlbController { } /// Check how many times has the limit of `hugetlb_size` hugepages been hit. - pub fn failcnt(&self, hugetlb_size: &String) -> Result { + pub fn failcnt(&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, hugetlb_size: &String) -> Result { + pub fn limit_in_bytes(&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, hugetlb_size: &String) -> Result { + pub fn usage_in_bytes(&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, hugetlb_size: &String) -> Result { + pub fn max_usage_in_bytes(&self, hugetlb_size: &String) -> Result { self.open_path( &format!("hugetlb.{}.max_usage_in_bytes", hugetlb_size), false, @@ -128,11 +130,11 @@ impl HugeTlbController { /// 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, hugetlb_size: &String, limit: u64) -> Result<(), CgroupError> { + pub fn set_limit_in_bytes(&self, hugetlb_size: &String, limit: u64) -> Result<()> { self.open_path(&format!("hugetlb.{}.limit_in_bytes", hugetlb_size), false) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } } diff --git a/src/lib.rs b/src/lib.rs index f827145..352dc64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub mod cpu; pub mod cpuacct; pub mod cpuset; pub mod devices; +pub mod error; pub mod freezer; pub mod hierarchies; pub mod hugetlb; @@ -26,6 +27,7 @@ use cpu::CpuController; use cpuacct::CpuAcctController; use cpuset::CpuSetController; use devices::DevicesController; +use error::*; use freezer::FreezerController; use hugetlb::HugeTlbController; use memory::MemController; @@ -68,38 +70,6 @@ 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, - /// An unknown error has occured. - Unknown, -} - -impl PartialEq for CgroupError { - fn eq(&self, other: &CgroupError) -> bool { - use std::mem::discriminant; - discriminant(&self) == discriminant(&other) - } -} - #[doc(hidden)] #[derive(Eq, PartialEq, Debug)] pub enum Controllers { @@ -144,7 +114,7 @@ impl Controllers { 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>; + fn apply(&self, res: &Resources) -> Result<()>; // meta stuff #[doc(hidden)] @@ -157,17 +127,21 @@ pub trait Controller { fn get_base(&self) -> &PathBuf; #[doc(hidden)] - fn verify_path(&self) -> bool { - self.get_path().starts_with(self.get_base()) + fn verify_path(&self) -> Result<()> { + if self.get_path().starts_with(self.get_base()) { + Ok(()) + } else { + Err(Error::new(ErrorKind::InvalidPath)) + } } /// Create this controller fn create(&self) { - if self.verify_path() { - match ::std::fs::create_dir(self.get_path()) { - Ok(_) => (), - Err(e) => warn!("error create_dir {:?}", e), - } + self.verify_path().expect("path should be valid"); + + match ::std::fs::create_dir(self.get_path()) { + Ok(_) => (), + Err(e) => warn!("error create_dir {:?}", e), } } @@ -184,22 +158,20 @@ pub trait Controller { } #[doc(hidden)] - fn open_path(&self, p: &str, w: bool) -> Result { + 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); - } + self.verify_path()?; if w { match File::create(&path) { - Err(e) => return Err(CgroupError::WriteError(e)), + Err(e) => return Err(Error::with_cause(ErrorKind::WriteFailed, e)), Ok(file) => return Ok(file), } } else { match File::open(&path) { - Err(e) => return Err(CgroupError::ReadError(e)), + Err(e) => return Err(Error::with_cause(ErrorKind::ReadFailed, e)), Ok(file) => return Ok(file), } } @@ -207,7 +179,7 @@ pub trait Controller { #[doc(hidden)] fn path_exists(&self, p: &str) -> bool { - if !self.verify_path() { + if let Err(_) = self.verify_path() { return false; } @@ -215,10 +187,10 @@ pub trait Controller { } /// Attach a task to this controller. - fn add_task(&self, pid: &CgroupPid) -> Result<(), CgroupError> { + 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(CgroupError::WriteError) + .map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e)) }) } diff --git a/src/memory.rs b/src/memory.rs index 98f57c4..7f32b84 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, MemoryResources, Resources, Subsystem, + ControllIdentifier, Controller, Controllers, MemoryResources, Resources, Subsystem, }; /// A controller that allows controlling the `memory` subsystem of a Cgroup. @@ -33,7 +35,7 @@ pub struct OomControl { pub oom_kill: u64, } -fn parse_oom_control(s: String) -> Result { +fn parse_oom_control(s: String) -> Result { let spl = s.split_whitespace().collect::>(); Ok(OomControl { @@ -81,7 +83,7 @@ pub struct NumaStat { pub hierarchical_unevictable_pages_per_node: Vec, } -fn parse_numa_stat(s: String) -> Result { +fn parse_numa_stat(s: String) -> Result { // Parse the number of nodes let nodes = (s.split_whitespace().collect::>().len() - 8) / 8; let mut ls = s.lines(); @@ -250,7 +252,7 @@ pub struct MemoryStat { pub total_unevictable: u64, } -fn parse_memory_stat(s: String) -> Result { +fn parse_memory_stat(s: String) -> Result { let sp: Vec<&str> = s .split_whitespace() .filter(|x| x.parse::().is_ok()) @@ -405,7 +407,7 @@ impl Controller for MemController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let memres: &MemoryResources = &res.memory; @@ -563,38 +565,38 @@ impl MemController { } /// Set the memory usage limit of the control group, in bytes. - pub fn set_limit(&self, limit: u64) -> Result<(), CgroupError> { + pub fn set_limit(&self, limit: u64) -> Result<()> { self.open_path("memory.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Set the kernel memory limit of the control group, in bytes. - pub fn set_kmem_limit(&self, limit: u64) -> Result<(), CgroupError> { + pub fn set_kmem_limit(&self, limit: u64) -> Result<()> { self.open_path("memory.kmem.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Set the memory+swap limit of the control group, in bytes. - pub fn set_memswap_limit(&self, limit: u64) -> Result<(), CgroupError> { + pub fn set_memswap_limit(&self, limit: u64) -> Result<()> { self.open_path("memory.memsw.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Set how much kernel memory can be used for TCP-related buffers by the control group. - pub fn set_tcp_limit(&self, limit: u64) -> Result<(), CgroupError> { + pub fn set_tcp_limit(&self, limit: u64) -> Result<()> { self.open_path("memory.kmem.tcp.limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -602,11 +604,11 @@ 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, limit: u64) -> Result<(), CgroupError> { + pub fn set_soft_limit(&self, limit: u64) -> Result<()> { self.open_path("memory.soft_limit_in_bytes", true) .and_then(|mut file| { file.write_all(limit.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } @@ -614,11 +616,11 @@ 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, swp: u64) -> Result<(), CgroupError> { + pub fn set_swappiness(&self, swp: u64) -> Result<()> { self.open_path("memory.swappiness", true) .and_then(|mut file| { file.write_all(swp.to_string().as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } } @@ -643,19 +645,19 @@ impl<'a> From<&'a Subsystem> for &'a MemController { } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } -fn read_string_from(mut file: File) -> Result { +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)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -723,9 +725,10 @@ total_unevictable 81920 #[test] fn test_parse_numa_stat() { + let ok = parse_numa_stat(GOOD_VALUE.to_string()).unwrap(); assert_eq!( - parse_numa_stat(GOOD_VALUE.to_string()), - Ok(NumaStat { + ok, + NumaStat { total_pages: 51189, total_pages_per_node: vec![51189, 123], file_pages: 50175, @@ -743,27 +746,29 @@ total_unevictable 81920 hierarchical_anon_pages_per_node: vec![770402, 123], hierarchical_unevictable_pages: 20, hierarchical_unevictable_pages_per_node: vec![20, 123], - }) + } ); } #[test] fn test_parse_oom_control() { + let ok = parse_oom_control(GOOD_OOMCONTROL_VAL.to_string()).unwrap(); assert_eq!( - parse_oom_control(GOOD_OOMCONTROL_VAL.to_string()), - Ok(OomControl { + ok, + OomControl { oom_kill_disable: false, under_oom: true, oom_kill: 1337, - }) + } ); } #[test] fn test_parse_memory_stat() { + let ok = parse_memory_stat(GOOD_MEMORYSTAT_VAL.to_string()).unwrap(); assert_eq!( - parse_memory_stat(GOOD_MEMORYSTAT_VAL.to_string()), - Ok(MemoryStat { + ok, + MemoryStat { cache: 178880512, rss: 4206592, rss_huge: 0, @@ -800,7 +805,7 @@ total_unevictable 81920 total_inactive_file: 1272135680, total_active_file: 2338816000, total_unevictable: 81920, - }) + } ); } } diff --git a/src/net_cls.rs b/src/net_cls.rs index 0ede9c9..52e24d6 100644 --- a/src/net_cls.rs +++ b/src/net_cls.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, NetworkResources, Resources, + ControllIdentifier, Controller, Controllers, NetworkResources, Resources, Subsystem, }; @@ -37,14 +39,14 @@ impl Controller for NetClsController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &NetworkResources = &res.network; if res.update_values { let _ = self.set_class(res.class_id); - if self.get_class() != Ok(res.class_id) { - return Err(CgroupError::Unknown); + if self.get_class()? != res.class_id { + return Err(Error::new(Other)); } } return Ok(()); @@ -71,11 +73,11 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController { } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -91,16 +93,16 @@ impl NetClsController { } /// Set the network class id of the outgoing packets of the control group's tasks. - pub fn set_class(&self, class: u64) -> Result<(), CgroupError> { + pub fn set_class(&self, class: u64) -> Result<()> { self.open_path("net_cls.classid", true) .and_then(|mut file| { let s = format!("{:#08X}", class); - file.write_all(s.as_ref()).map_err(CgroupError::WriteError) + file.write_all(s.as_ref()).map_err(|e| Error::with_cause(WriteFailed, e)) }) } /// Get the network class id of the outgoing packets of the control group's tasks. - pub fn get_class(&self) -> Result { + pub fn get_class(&self) -> Result { self.open_path("net_cls.classid", false) .and_then(|file| read_u64_from(file)) } diff --git a/src/net_prio.rs b/src/net_prio.rs index 096f3d3..cf21351 100644 --- a/src/net_prio.rs +++ b/src/net_prio.rs @@ -7,9 +7,11 @@ use std::fs::File; use std::io::{BufRead, BufReader, Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, NetworkResources, Resources, + ControllIdentifier, Controller, Controllers, NetworkResources, Resources, Subsystem, }; @@ -38,7 +40,7 @@ impl Controller for NetPrioController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let res: &NetworkResources = &res.network; @@ -72,11 +74,11 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController { } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -99,7 +101,7 @@ impl NetPrioController { } /// A map of priorities for each network interface. - pub fn ifpriomap(&self) -> Result, CgroupError> { + pub fn ifpriomap(&self) -> Result> { self.open_path("net_prio.ifpriomap", false) .and_then(|file| { let bf = BufReader::new(file); @@ -113,15 +115,16 @@ impl NetPrioController { let ifname = sp.nth(0); let ifprio = sp.nth(1); if ifname.is_none() || ifprio.is_none() { - Err(CgroupError::ParseError) + Err(Error::new(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) + match ifprio { + Err(e) => Err(Error::with_cause(ParseError, e)), + Ok(_) => { + acc.insert(ifname.to_string(), ifprio.unwrap()); + Ok(acc) + } } } } @@ -130,11 +133,11 @@ impl NetPrioController { } /// Set the priority of the network traffic on `eif` to be `prio`. - pub fn set_if_prio(&self, eif: &String, prio: u64) -> Result<(), CgroupError> { + pub fn set_if_prio(&self, eif: &String, prio: u64) -> Result<()> { self.open_path("net_prio.ifpriomap", true) .and_then(|mut file| { file.write_all(format!("{} {}", eif, prio).as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } } diff --git a/src/perf_event.rs b/src/perf_event.rs index 314dd94..33fb41e 100644 --- a/src/perf_event.rs +++ b/src/perf_event.rs @@ -4,7 +4,9 @@ //! [tools/perf/Documentation/perf-record.txt](https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/perf-record.txt) use std::path::PathBuf; -use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem}; +use error::*; + +use {ControllIdentifier, Controller, Controllers, Resources, Subsystem}; /// A controller that allows controlling the `perf_event` subsystem of a Cgroup. /// @@ -30,7 +32,7 @@ impl Controller for PerfEventController { &self.base } - fn apply(&self, _res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, _res: &Resources) -> Result<()> { Ok(()) } } diff --git a/src/pid.rs b/src/pid.rs index a0a8d34..3de1c2a 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -6,9 +6,11 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use CgroupError::*; +use error::*; +use error::ErrorKind::*; + use { - CgroupError, ControllIdentifier, Controller, Controllers, PidResources, Resources, Subsystem, + ControllIdentifier, Controller, Controllers, PidResources, Resources, Subsystem, }; /// A controller that allows controlling the `pids` subsystem of a Cgroup. @@ -48,7 +50,7 @@ impl Controller for PidController { &self.base } - fn apply(&self, res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, res: &Resources) -> Result<()> { // get the resources that apply to this controller let pidres: &PidResources = &res.pid; @@ -57,10 +59,10 @@ impl Controller for PidController { let _ = self.set_pid_max(pidres.maximum_number_of_processes); // now, verify - if self.get_pid_max() == Ok(pidres.maximum_number_of_processes) { + if self.get_pid_max()? == pidres.maximum_number_of_processes { return Ok(()); } else { - return Err(CgroupError::Unknown); + return Err(Error::new(Other)); } } @@ -94,11 +96,11 @@ impl<'a> From<&'a Subsystem> for &'a PidController { } } -fn read_u64_from(mut file: File) -> Result { +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)), + Ok(_) => string.trim().parse().map_err(|e| Error::with_cause(ParseError, e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -115,30 +117,30 @@ impl PidController { } /// The number of times `fork` failed because the limit was hit. - pub fn get_pid_events(&self) -> Result { + pub fn get_pid_events(&self) -> Result { self.open_path("pids.events", false).and_then(|mut file| { let mut string = String::new(); 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), + Err(e) => Err(Error::with_cause(ParseError, e)), }, - None => Err(CgroupError::ParseError), + None => Err(Error::new(ParseError)), }, - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } }) } /// The number of processes currently. - pub fn get_pid_current(&self) -> Result { + pub fn get_pid_current(&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) -> Result { + pub fn get_pid_max(&self) -> Result { self.open_path("pids.max", false).and_then(|mut file| { let mut string = String::new(); let res = file.read_to_string(&mut string); @@ -148,10 +150,10 @@ impl PidController { } else { match string.trim().parse() { Ok(val) => Ok(PidMax::Value(val)), - Err(_) => Err(CgroupError::ParseError), + Err(e) => Err(Error::with_cause(ParseError, e)), } }, - Err(e) => Err(CgroupError::ReadError(e)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } }) } @@ -161,7 +163,7 @@ 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, max_pid: PidMax) -> Result<(), CgroupError> { + pub fn set_pid_max(&self, max_pid: PidMax) -> Result<()> { self.open_path("pids.max", true).and_then(|mut file| { let string_to_write = match max_pid { PidMax::Max => "max".to_string(), @@ -169,7 +171,7 @@ impl PidController { }; match file.write_all(string_to_write.as_ref()) { Ok(_) => Ok(()), - Err(e) => Err(CgroupError::WriteError(e)), + Err(e) => Err(Error::with_cause(WriteFailed, e)), } }) } diff --git a/src/rdma.rs b/src/rdma.rs index 8163164..165da6b 100644 --- a/src/rdma.rs +++ b/src/rdma.rs @@ -6,7 +6,10 @@ use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; -use {CgroupError, ControllIdentifier, Controller, Controllers, Resources, Subsystem}; +use error::*; +use error::ErrorKind::*; + +use {ControllIdentifier, Controller, Controllers, Resources, Subsystem}; /// A controller that allows controlling the `rdma` subsystem of a Cgroup. /// @@ -32,7 +35,7 @@ impl Controller for RdmaController { &self.base } - fn apply(&self, _res: &Resources) -> Result<(), CgroupError> { + fn apply(&self, _res: &Resources) -> Result<()> { Ok(()) } } @@ -57,11 +60,11 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController { } } -fn read_string_from(mut file: File) -> Result { +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)), + Err(e) => Err(Error::with_cause(ReadFailed, e)), } } @@ -77,16 +80,16 @@ impl RdmaController { } /// Returns the current usage of RDMA/IB specific resources. - pub fn current(&self) -> Result { + pub fn current(&self) -> Result { self.open_path("rdma.current", false) .and_then(read_string_from) } /// Set a maximum usage for each RDMA/IB resource. - pub fn set_max(&self, max: &String) -> Result<(), CgroupError> { + pub fn set_max(&self, max: &String) -> Result<()> { self.open_path("rdma.max", true).and_then(|mut file| { file.write_all(max.as_ref()) - .map_err(CgroupError::WriteError) + .map_err(|e| Error::with_cause(WriteFailed, e)) }) } } diff --git a/tests/cpuset.rs b/tests/cpuset.rs index b456415..79eaf5b 100644 --- a/tests/cpuset.rs +++ b/tests/cpuset.rs @@ -1,7 +1,8 @@ extern crate cgroups; use cgroups::cpuset::CpuSetController; -use cgroups::{Cgroup, CgroupError}; +use cgroups::error::ErrorKind; +use cgroups::Cgroup; #[test] fn test_cpuset_memory_pressure_root_cg() { @@ -12,8 +13,7 @@ fn test_cpuset_memory_pressure_root_cg() { // This is not a root control group, so it should fail via InvalidOperation. let res = cpuset.set_enable_memory_pressure(true); - assert!(res.is_err()); - assert_eq!(res.unwrap_err(), CgroupError::InvalidOperation); + assert_eq!(res.unwrap_err().kind(), &ErrorKind::InvalidOperation); } cg.delete(); } diff --git a/tests/pids.rs b/tests/pids.rs index 2a6a3a0..248bb34 100644 --- a/tests/pids.rs +++ b/tests/pids.rs @@ -2,7 +2,7 @@ extern crate cgroups; use cgroups::pid::{PidController, PidMax}; use cgroups::Controller; -use cgroups::{Cgroup, CgroupError, CgroupPid, PidResources, Resources}; +use cgroups::{Cgroup, CgroupPid, PidResources, Resources}; extern crate nix; use nix::sys::wait::{waitpid, WaitStatus};