diff --git a/Cargo.toml b/Cargo.toml index 9a7897a..296fc16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,10 @@ readme = "README.md" [dependencies] log = "0.4" regex = "1.1" -nix = { version = "0.24", default-features = false, features = ["event", "fs", "process"] } +nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] } libc = "0.2" serde = { version = "1.0", features = ["derive"], optional = true } +thiserror = "1" [dev-dependencies] libc = "0.2.76" diff --git a/src/blkio.rs b/src/blkio.rs index 58d3d3c..87f16df 100644 --- a/src/blkio.rs +++ b/src/blkio.rs @@ -665,8 +665,12 @@ impl BlkIoController { 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(|e| Error::with_cause(WriteFailed, e)) + file.write_all(w.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("blkio.leaf_weight".to_string(), w.to_string()), + e, + ) + }) }) } @@ -675,7 +679,15 @@ impl BlkIoController { self.open_path("blkio.leaf_weight_device", true) .and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, weight).as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| { + Error::with_cause( + WriteFailed( + "blkio.leaf_weight_device".to_string(), + format!("{}:{} {}", major, minor, weight), + ), + e, + ) + }) }) } @@ -683,95 +695,112 @@ impl BlkIoController { pub fn reset_stats(&self) -> Result<()> { self.open_path("blkio.reset_stats", true) .and_then(|mut file| { - file.write_all("1".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("1".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("blkio.reset_stats".to_string(), "1".to_string()), + e, + ) + }) }) } /// 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, major: u64, minor: u64, bps: u64) -> Result<()> { - let mut file = "blkio.throttle.read_bps_device"; + let mut file_name = "blkio.throttle.read_bps_device"; let mut content = format!("{}:{} {}", major, minor, bps); if self.v2 { - file = "io.max"; + file_name = "io.max"; content = format!("{}:{} rbps={}", major, minor, bps); } - self.open_path(file, true).and_then(|mut file| { - file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(content.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e) + }) }) } /// 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, major: u64, minor: u64, iops: u64) -> Result<()> { - let mut file = "blkio.throttle.read_iops_device"; + let mut file_name = "blkio.throttle.read_iops_device"; let mut content = format!("{}:{} {}", major, minor, iops); if self.v2 { - file = "io.max"; + file_name = "io.max"; content = format!("{}:{} riops={}", major, minor, iops); } - self.open_path(file, true).and_then(|mut file| { - file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(content.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e) + }) }) } /// 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, major: u64, minor: u64, bps: u64) -> Result<()> { - let mut file = "blkio.throttle.write_bps_device"; + let mut file_name = "blkio.throttle.write_bps_device"; let mut content = format!("{}:{} {}", major, minor, bps); if self.v2 { - file = "io.max"; + file_name = "io.max"; content = format!("{}:{} wbps={}", major, minor, bps); } - self.open_path(file, true).and_then(|mut file| { - file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(content.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e) + }) }) } /// 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, major: u64, minor: u64, iops: u64) -> Result<()> { - let mut file = "blkio.throttle.write_iops_device"; + let mut file_name = "blkio.throttle.write_iops_device"; let mut content = format!("{}:{} {}", major, minor, iops); if self.v2 { - file = "io.max"; + file_name = "io.max"; content = format!("{}:{} wiops={}", major, minor, iops); } - self.open_path(file, true).and_then(|mut file| { - file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(content.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), content.to_string()), e) + }) }) } /// Set the weight of the control group's tasks. pub fn set_weight(&self, w: u64) -> Result<()> { // Attation: may not find in high kernel version. - let mut file = "blkio.weight"; + let mut file_name = "blkio.weight"; if self.v2 { - file = "io.bfq.weight"; + file_name = "io.bfq.weight"; } - self.open_path(file, true).and_then(|mut file| { - file.write_all(w.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(w.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), w.to_string()), e) + }) }) } /// Same as `set_weight()`, but settable per each block device. pub fn set_weight_for_device(&self, major: u64, minor: u64, weight: u64) -> Result<()> { - let mut file = "blkio.weight_device"; + let mut file_name = "blkio.weight_device"; if self.v2 { // Attation: there is no weight for device in runc // https://github.com/opencontainers/runc/blob/46be7b612e2533c494e6a251111de46d8e286ed5/libcontainer/cgroups/fs2/io.go#L30 // may depends on IO schedulers https://wiki.ubuntu.com/Kernel/Reference/IOSchedulers - file = "io.bfq.weight"; + file_name = "io.bfq.weight"; } - self.open_path(file, true).and_then(|mut file| { + self.open_path(file_name, true).and_then(|mut file| { file.write_all(format!("{}:{} {}", major, minor, weight).as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| { + Error::with_cause( + WriteFailed( + file_name.to_string(), + format!("{}:{} {}", major, minor, weight), + ), + e, + ) + }) }) } } diff --git a/src/cgroup.rs b/src/cgroup.rs index f37c07b..9750344 100644 --- a/src/cgroup.rs +++ b/src/cgroup.rs @@ -370,7 +370,8 @@ pub fn get_cgroups_relative_paths_by_pid(pid: u32) -> Result Result> { let mut m = HashMap::new(); - let content = fs::read_to_string(path).map_err(|e| Error::with_cause(ReadFailed, e))?; + let content = + fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?; for l in content.lines() { let fl: Vec<&str> = l.split(':').collect(); if fl.len() != 3 { diff --git a/src/cpu.rs b/src/cpu.rs index d3dc9c0..34cdfe9 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -131,7 +131,7 @@ impl CpuController { let res = file.read_to_string(&mut s); match res { Ok(_) => Ok(s), - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed("cpu.stat".to_string()), e)), } }) .unwrap_or_default(), @@ -145,14 +145,15 @@ impl CpuController { /// `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<()> { - let mut file = "cpu.shares"; + let mut file_name = "cpu.shares"; if self.v2 { - file = "cpu.weight"; + file_name = "cpu.weight"; } // NOTE: .CpuShares is not used here. Conversion is the caller's responsibility. - self.open_path(file, true).and_then(|mut file| { - file.write_all(shares.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(shares.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), shares.to_string()), e) + }) }) } @@ -174,8 +175,12 @@ impl CpuController { } self.open_path("cpu.cfs_period_us", true) .and_then(|mut file| { - file.write_all(us.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(us.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("cpu.cfs_period_us".to_string(), us.to_string()), + e, + ) + }) }) } @@ -200,8 +205,12 @@ impl CpuController { } self.open_path("cpu.cfs_quota_us", true) .and_then(|mut file| { - file.write_all(us.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(us.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("cpu.cfs_quota_us".to_string(), us.to_string()), + e, + ) + }) }) } @@ -262,23 +271,31 @@ impl CpuController { let line = format!("{} {}", new_quota, new_period); self.open_path("cpu.max", true).and_then(|mut file| { file.write_all(line.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| Error::with_cause(WriteFailed("cpu.max".to_string(), line), e)) }) } pub fn set_rt_runtime(&self, us: i64) -> Result<()> { self.open_path("cpu.rt_runtime_us", true) .and_then(|mut file| { - file.write_all(us.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(us.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("cpu.rt_runtime_us".to_string(), us.to_string()), + e, + ) + }) }) } pub fn set_rt_period_us(&self, us: u64) -> Result<()> { self.open_path("cpu.rt_period_us", true) .and_then(|mut file| { - file.write_all(us.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(us.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("cpu.rt_period_us".to_string(), us.to_string()), + e, + ) + }) }) } } @@ -288,7 +305,7 @@ impl CustomizedAttribute for CpuController {} fn parse_cfs_quota_and_period(mut file: File) -> Result { let mut content = String::new(); file.read_to_string(&mut content) - .map_err(|e| Error::with_cause(ReadFailed, e))?; + .map_err(|e| Error::with_cause(ReadFailed("cpu.max".to_string()), e))?; let fields = content.trim().split(' ').collect::>(); if fields.len() != 2 { diff --git a/src/cpuacct.rs b/src/cpuacct.rs index 700004a..8c238be 100644 --- a/src/cpuacct.rs +++ b/src/cpuacct.rs @@ -148,8 +148,9 @@ impl CpuAcctController { /// Reset the statistics the kernel has gathered about the control group. pub fn reset(&self) -> Result<()> { self.open_path("cpuacct.usage", true).and_then(|mut file| { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause(WriteFailed("cpuacct.usage".to_string(), "0".to_string()), e) + }) }) } } diff --git a/src/cpuset.rs b/src/cpuset.rs index 9a96398..de411b2 100644 --- a/src/cpuset.rs +++ b/src/cpuset.rs @@ -144,7 +144,12 @@ fn find_no_empty_parent(from: &str, file: &str) -> Result<(String, Vec) let current_value = match ::std::fs::read_to_string(current_path.clone().join(file).to_str().unwrap()) { Ok(cpus) => String::from(cpus.trim()), - Err(e) => return Err(Error::with_cause(ReadFailed, e)), + Err(e) => { + return Err(Error::with_cause( + ReadFailed(current_path.display().to_string()), + e, + )) + } }; if !current_value.is_empty() { @@ -177,7 +182,12 @@ fn copy_from_parent(current: &str, file: &str) -> Result<()> { pb.push(file); match ::std::fs::write(pb.to_str().unwrap(), value.as_bytes()) { Ok(_) => (), - Err(e) => return Err(Error::with_cause(WriteFailed, e)), + Err(e) => { + return Err(Error::with_cause( + WriteFailed(pb.display().to_string(), pb.display().to_string()), + e, + )) + } } } @@ -347,11 +357,19 @@ impl CpuSetController { self.open_path("cpuset.cpu_exclusive", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.cpu_exclusive".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.cpu_exclusive".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -362,11 +380,19 @@ impl CpuSetController { self.open_path("cpuset.mem_exclusive", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.mem_exclusive".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.mem_exclusive".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -377,8 +403,9 @@ impl CpuSetController { /// be represented via dashes. pub fn set_cpus(&self, cpus: &str) -> Result<()> { self.open_path("cpuset.cpus", true).and_then(|mut file| { - file.write_all(cpus.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(cpus.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("cpuset.cpus".to_string(), cpus.to_string()), e) + }) }) } @@ -387,8 +414,9 @@ impl CpuSetController { /// Syntax is the same as with `set_cpus()`. pub fn set_mems(&self, mems: &str) -> Result<()> { self.open_path("cpuset.mems", true).and_then(|mut file| { - file.write_all(mems.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(mems.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("cpuset.mems".to_string(), mems.to_string()), e) + }) }) } @@ -401,11 +429,19 @@ impl CpuSetController { self.open_path("cpuset.mem_hardwall", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.mem_hardwall".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.mem_hardwall".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -416,11 +452,19 @@ impl CpuSetController { self.open_path("cpuset.sched_load_balance", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.sched_load_balance".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.sched_load_balance".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -431,8 +475,12 @@ impl CpuSetController { 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(|e| Error::with_cause(WriteFailed, e)) + file.write_all(i.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.sched_relax_domain_level".to_string(), i.to_string()), + e, + ) + }) }) } @@ -442,11 +490,19 @@ impl CpuSetController { self.open_path("cpuset.memory_migrate", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_migrate".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_migrate".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -457,11 +513,19 @@ impl CpuSetController { self.open_path("cpuset.memory_spread_page", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_spread_page".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_spread_page".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -472,11 +536,19 @@ impl CpuSetController { self.open_path("cpuset.memory_spread_slab", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_spread_slab".to_string(), "1".to_string()), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed("cpuset.memory_spread_slab".to_string(), "0".to_string()), + e, + ) + }) } }) } @@ -493,11 +565,25 @@ impl CpuSetController { self.open_path("cpuset.memory_pressure_enabled", true) .and_then(|mut file| { if b { - file.write_all(b"1") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"1").map_err(|e| { + Error::with_cause( + WriteFailed( + "cpuset.memory_pressure_enabled".to_string(), + "1".to_string(), + ), + e, + ) + }) } else { - file.write_all(b"0") - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(b"0").map_err(|e| { + Error::with_cause( + WriteFailed( + "cpuset.memory_pressure_enabled".to_string(), + "0".to_string(), + ), + e, + ) + }) } }) } diff --git a/src/devices.rs b/src/devices.rs index 60e5675..fffa879 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -237,8 +237,9 @@ 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(|e| Error::with_cause(WriteFailed, e)) + file.write_all(final_str.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("devices.allow".to_string(), final_str), e) + }) }) } @@ -269,8 +270,9 @@ 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(|e| Error::with_cause(WriteFailed, e)) + file.write_all(final_str.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("devices.deny".to_string(), final_str), e) + }) }) } @@ -315,7 +317,7 @@ impl DevicesController { } }) }, - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)), } }) } diff --git a/src/error.rs b/src/error.rs index cea6a39..95fe5f5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,40 +8,51 @@ 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)] +#[derive(thiserror::Error, Debug, Eq, PartialEq)] pub enum ErrorKind { + #[error("fs error")] FsError, + + #[error("common error: {0}")] Common(String), /// An error occured while writing to a control group file. - WriteFailed, + #[error("unable to write to a control group file {0}, value {1}")] + WriteFailed(String, String), /// An error occured while trying to read from a control group file. - ReadFailed, + #[error("unable to read a control group file {0}")] + ReadFailed(String), /// An error occured while trying to remove a control group. + #[error("unable to remove a control group")] RemoveFailed, /// 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. + #[error("unable to parse control group file")] 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. + #[error("the requested operation is invalid")] 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. + #[error("the given path is invalid")] InvalidPath, + #[error("invalid bytes size")] InvalidBytesSize, /// An unknown error has occured. + #[error("an unknown error")] Other, } @@ -53,23 +64,10 @@ pub struct Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match &self.kind { - ErrorKind::FsError => "fs error".to_string(), - ErrorKind::Common(s) => s.clone(), - ErrorKind::WriteFailed => "unable to write to a control group file".to_string(), - ErrorKind::ReadFailed => "unable to read a control group file".to_string(), - ErrorKind::RemoveFailed => "unable to remove a control group".to_string(), - ErrorKind::ParseError => "unable to parse control group file".to_string(), - ErrorKind::InvalidOperation => "the requested operation is invalid".to_string(), - ErrorKind::InvalidPath => "the given path is invalid".to_string(), - ErrorKind::InvalidBytesSize => "invalid bytes size".to_string(), - ErrorKind::Other => "an unknown error".to_string(), - }; - if let Some(cause) = &self.cause { - write!(f, "{} caused by: {:?}", msg, cause) + write!(f, "{} caused by: {:?}", &self.kind, cause) } else { - write!(f, "{}", msg) + write!(f, "{}", &self.kind) } } } diff --git a/src/events.rs b/src/events.rs index f3a6dbe..65506c0 100644 --- a/src/events.rs +++ b/src/events.rs @@ -46,10 +46,11 @@ fn register_memory_event( arg: &str, ) -> Result> { let path = cg_dir.join(event_name); - let event_file = File::open(path).map_err(|e| Error::with_cause(ReadFailed, e))?; + let event_file = File::open(path.clone()) + .map_err(|e| Error::with_cause(ReadFailed(path.display().to_string()), e))?; - let eventfd = - eventfd(0, EfdFlags::EFD_CLOEXEC).map_err(|e| Error::with_cause(ReadFailed, e))?; + let eventfd = eventfd(0, EfdFlags::EFD_CLOEXEC) + .map_err(|e| Error::with_cause(ReadFailed("eventfd".to_string()), e))?; let event_control_path = cg_dir.join("cgroup.event_control"); let data = if arg.is_empty() { @@ -59,7 +60,12 @@ fn register_memory_event( }; // write to file and set mode to 0700(FIXME) - fs::write(&event_control_path, data).map_err(|e| Error::with_cause(WriteFailed, e))?; + fs::write(&event_control_path, data.clone()).map_err(|e| { + Error::with_cause( + WriteFailed(event_control_path.display().to_string(), data), + e, + ) + })?; let mut eventfd_file = unsafe { File::from_raw_fd(eventfd) }; diff --git a/src/freezer.rs b/src/freezer.rs index 4360ba2..310af30 100644 --- a/src/freezer.rs +++ b/src/freezer.rs @@ -94,40 +94,40 @@ impl FreezerController { /// Freezes the processes in the control group. pub fn freeze(&self) -> Result<()> { - let mut file = "freezer.state"; + let mut file_name = "freezer.state"; let mut content = "FROZEN".to_string(); if self.v2 { - file = "cgroup.freeze"; + file_name = "cgroup.freeze"; content = "1".to_string(); } - self.open_path(file, true).and_then(|mut file| { + self.open_path(file_name, true).and_then(|mut file| { file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), content), e)) }) } /// Thaws, that is, unfreezes the processes in the control group. pub fn thaw(&self) -> Result<()> { - let mut file = "freezer.state"; + let mut file_name = "freezer.state"; let mut content = "THAWED".to_string(); if self.v2 { - file = "cgroup.freeze"; + file_name = "cgroup.freeze"; content = "0".to_string(); } - self.open_path(file, true).and_then(|mut file| { + self.open_path(file_name, true).and_then(|mut file| { file.write_all(content.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), content), e)) }) } /// Retrieve the state of processes in the control group. pub fn state(&self) -> Result { - let mut file = "freezer.state"; + let mut file_name = "freezer.state"; if self.v2 { - file = "cgroup.freeze"; + file_name = "cgroup.freeze"; } - self.open_path(file, false).and_then(|mut file| { + self.open_path(file_name, false).and_then(|mut file| { let mut s = String::new(); let res = file.read_to_string(&mut s); match res { @@ -139,7 +139,7 @@ impl FreezerController { "FREEZING" => Ok(FreezerState::Freezing), _ => Err(Error::new(ParseError)), }, - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed(file_name.to_string()), e)), } }) } diff --git a/src/hugetlb.rs b/src/hugetlb.rs index f215a7e..1ccc9ee 100644 --- a/src/hugetlb.rs +++ b/src/hugetlb.rs @@ -165,13 +165,14 @@ 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: &str, limit: u64) -> Result<()> { - let mut file = format!("hugetlb.{}.limit_in_bytes", hugetlb_size); + let mut file_name = format!("hugetlb.{}.limit_in_bytes", hugetlb_size); if self.v2 { - file = format!("hugetlb.{}.max", hugetlb_size); + file_name = format!("hugetlb.{}.max", hugetlb_size); } - self.open_path(&file, true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(&file_name, true).and_then(|mut file| { + file.write_all(limit.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) + }) }) } } diff --git a/src/lib.rs b/src/lib.rs index dc31bf7..2889344 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,12 +181,21 @@ mod sealed { if w { match File::create(&path) { - Err(e) => Err(Error::with_cause(ErrorKind::WriteFailed, e)), + Err(e) => Err(Error::with_cause( + ErrorKind::WriteFailed( + path.display().to_string(), + "[CREATE FILE]".to_string(), + ), + e, + )), Ok(file) => Ok(file), } } else { match File::open(&path) { - Err(e) => Err(Error::with_cause(ErrorKind::ReadFailed, e)), + Err(e) => Err(Error::with_cause( + ErrorKind::ReadFailed(path.display().to_string()), + e, + )), Ok(file) => Ok(file), } } @@ -198,7 +207,7 @@ mod sealed { let res = file.read_to_string(&mut string); match res { Ok(_) => parse_max_value(&string), - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed(f.to_string()), e)), } }) } @@ -216,8 +225,9 @@ mod sealed { pub trait CustomizedAttribute: ControllerInternal { fn set(&self, key: &str, value: &str) -> Result<()> { self.open_path(key, true).and_then(|mut file| { - file.write_all(value.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(value.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(key.to_string(), value.to_string()), e) + }) }) } @@ -226,7 +236,7 @@ mod sealed { let mut string = String::new(); match file.read_to_string(&mut string) { Ok(_) => Ok(string.trim().to_owned()), - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed(key.to_string()), e)), } }) } @@ -309,16 +319,24 @@ where fn set_notify_on_release(&self, enable: bool) -> Result<()> { self.open_path("notify_on_release", true) .and_then(|mut file| { - write!(file, "{}", enable as i32) - .map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e)) + write!(file, "{}", enable as i32).map_err(|e| { + Error::with_cause( + ErrorKind::WriteFailed("notify_on_release".to_string(), enable.to_string()), + e, + ) + }) }) } /// Set release_agent fn set_release_agent(&self, path: &str) -> Result<()> { self.open_path("release_agent", true).and_then(|mut file| { - file.write_all(path.as_bytes()) - .map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e)) + file.write_all(path.as_bytes()).map_err(|e| { + Error::with_cause( + ErrorKind::WriteFailed("release_agent".to_string(), path.to_string()), + e, + ) + }) }) } /// Does this controller already exist? @@ -353,21 +371,29 @@ where /// Attach a task to this controller. fn add_task(&self, pid: &CgroupPid) -> Result<()> { - let mut file = "tasks"; + let mut file_name = "tasks"; if self.is_v2() { - file = "cgroup.procs"; + file_name = "cgroup.procs"; } - self.open_path(file, true).and_then(|mut file| { - file.write_all(pid.pid.to_string().as_ref()) - .map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(pid.pid.to_string().as_ref()).map_err(|e| { + Error::with_cause( + ErrorKind::WriteFailed(file_name.to_string(), pid.pid.to_string()), + e, + ) + }) }) } /// Attach a task to this controller by thread group id. fn add_task_by_tgid(&self, pid: &CgroupPid) -> Result<()> { self.open_path("cgroup.procs", true).and_then(|mut file| { - file.write_all(pid.pid.to_string().as_ref()) - .map_err(|e| Error::with_cause(ErrorKind::WriteFailed, e)) + file.write_all(pid.pid.to_string().as_ref()).map_err(|e| { + Error::with_cause( + ErrorKind::WriteFailed("cgroup.procs".to_string(), pid.pid.to_string()), + e, + ) + }) }) } @@ -409,8 +435,11 @@ fn remove_dir(dir: &Path) -> Result<()> { } if dir.exists() && dir.is_dir() { - for entry in fs::read_dir(dir).map_err(|e| Error::with_cause(ReadFailed, e))? { - let entry = entry.map_err(|e| Error::with_cause(ReadFailed, e))?; + for entry in fs::read_dir(dir) + .map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))? + { + let entry = + entry.map_err(|e| Error::with_cause(ReadFailed(dir.display().to_string()), e))?; let path = entry.path(); if path.is_dir() { remove_dir(&path)?; @@ -817,7 +846,7 @@ pub fn parse_max_value(s: &str) -> Result { pub fn flat_keyed_to_vec(mut file: File) -> Result> { let mut content = String::new(); file.read_to_string(&mut content) - .map_err(|e| Error::with_cause(ReadFailed, e))?; + .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?; let mut v = Vec::new(); for line in content.lines() { @@ -837,7 +866,7 @@ pub fn flat_keyed_to_vec(mut file: File) -> Result> { pub fn flat_keyed_to_hashmap(mut file: File) -> Result> { let mut content = String::new(); file.read_to_string(&mut content) - .map_err(|e| Error::with_cause(ReadFailed, e))?; + .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?; let mut h = HashMap::new(); for line in content.lines() { @@ -857,7 +886,7 @@ pub fn flat_keyed_to_hashmap(mut file: File) -> Result> { pub fn nested_keyed_to_hashmap(mut file: File) -> Result>> { let mut content = String::new(); file.read_to_string(&mut content) - .map_err(|e| Error::with_cause(ReadFailed, e))?; + .map_err(|e| Error::with_cause(ReadFailed("FIXME: read_string_from".to_string()), e))?; let mut h = HashMap::new(); for line in content.lines() { @@ -891,7 +920,10 @@ where .trim() .parse::() .map_err(|e| Error::with_cause(ParseError, e)), - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause( + ReadFailed("FIXME: can't get path in fn read_from".to_string()), + e, + )), } } @@ -899,7 +931,10 @@ 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(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause( + ReadFailed("FIXME: can't get path in fn read_string_from".to_string()), + e, + )), } } diff --git a/src/memory.rs b/src/memory.rs index 216af30..55477f7 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -562,8 +562,9 @@ impl MemController { if let Some(v) = v { let v = v.to_string(); self.open_path(f, true).and_then(|mut file| { - file.write_all(v.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(v.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(f.to_string(), format!("{:?}", v)), e) + }) })?; } } @@ -769,8 +770,12 @@ impl MemController { /// Reset the fail counter pub fn reset_fail_count(&self) -> Result<()> { self.open_path("memory.failcnt", true).and_then(|mut file| { - file.write_all("0".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("0".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.failcnt".to_string(), "0".to_string()), + e, + ) + }) }) } @@ -783,8 +788,12 @@ impl MemController { self.open_path("memory.kmem.failcnt", true) .and_then(|mut file| { - file.write_all("0".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("0".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.kmem.failcnt".to_string(), "0".to_string()), + e, + ) + }) }) } @@ -797,8 +806,12 @@ impl MemController { self.open_path("memory.kmem.tcp.failcnt", true) .and_then(|mut file| { - file.write_all("0".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("0".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.kmem.tcp.failcnt".to_string(), "0".to_string()), + e, + ) + }) }) } @@ -806,8 +819,12 @@ impl MemController { pub fn reset_memswap_fail_count(&self) -> Result<()> { self.open_path("memory.memsw.failcnt", true) .and_then(|mut file| { - file.write_all("0".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("0".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.memsw.failcnt".to_string(), "0".to_string()), + e, + ) + }) }) } @@ -815,20 +832,25 @@ impl MemController { pub fn reset_max_usage(&self) -> Result<()> { self.open_path("memory.max_usage_in_bytes", true) .and_then(|mut file| { - file.write_all("0".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("0".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.max_usage_in_bytes".to_string(), "0".to_string()), + e, + ) + }) }) } /// Set the memory usage limit of the control group, in bytes. pub fn set_limit(&self, limit: i64) -> Result<()> { - let mut file = "memory.limit_in_bytes"; + let mut file_name = "memory.limit_in_bytes"; if self.v2 { - file = "memory.max"; + file_name = "memory.max"; } - self.open_path(file, true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(limit.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) + }) }) } @@ -848,20 +870,24 @@ impl MemController { warn!("memory.kmem.limit_in_bytes is unsupported by the kernel"); Ok(()) } - Err(e) => Err(Error::with_cause(WriteFailed, e)), + Err(e) => Err(Error::with_cause( + WriteFailed("memory.kmem.limit_in_bytes".to_string(), limit.to_string()), + e, + )), } }) } /// Set the memory+swap limit of the control group, in bytes. pub fn set_memswap_limit(&self, limit: i64) -> Result<()> { - let mut file = "memory.memsw.limit_in_bytes"; + let mut file_name = "memory.memsw.limit_in_bytes"; if self.v2 { - file = "memory.swap.max"; + file_name = "memory.swap.max"; } - self.open_path(file, true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(limit.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) + }) }) } @@ -874,8 +900,15 @@ impl MemController { self.open_path("memory.kmem.tcp.limit_in_bytes", true) .and_then(|mut file| { - file.write_all(limit.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(limit.to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed( + "memory.kmem.tcp.limit_in_bytes".to_string(), + limit.to_string(), + ), + e, + ) + }) }) } @@ -884,13 +917,14 @@ 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: i64) -> Result<()> { - let mut file = "memory.soft_limit_in_bytes"; + let mut file_name = "memory.soft_limit_in_bytes"; if self.v2 { - file = "memory.low" + file_name = "memory.low" } - self.open_path(file, true).and_then(|mut file| { - file.write_all(limit.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(limit.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) + }) }) } @@ -899,22 +933,27 @@ impl MemController { /// /// 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<()> { - let mut file = "memory.swappiness"; + let mut file_name = "memory.swappiness"; if self.v2 { - file = "memory.swap.max" + file_name = "memory.swap.max" } - self.open_path(file, true).and_then(|mut file| { - file.write_all(swp.to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + self.open_path(file_name, true).and_then(|mut file| { + file.write_all(swp.to_string().as_ref()).map_err(|e| { + Error::with_cause(WriteFailed(file_name.to_string(), swp.to_string()), e) + }) }) } pub fn disable_oom_killer(&self) -> Result<()> { self.open_path("memory.oom_control", true) .and_then(|mut file| { - file.write_all("1".to_string().as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all("1".to_string().as_ref()).map_err(|e| { + Error::with_cause( + WriteFailed("memory.oom_control".to_string(), "1".to_string()), + e, + ) + }) }) } diff --git a/src/net_cls.rs b/src/net_cls.rs index fd6d133..dc5797e 100644 --- a/src/net_cls.rs +++ b/src/net_cls.rs @@ -88,8 +88,9 @@ impl NetClsController { self.open_path("net_cls.classid", true) .and_then(|mut file| { let s = format!("{:#08X}", class); - file.write_all(s.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(s.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("net_cls.classid".to_string(), s), e) + }) }) } diff --git a/src/net_prio.rs b/src/net_prio.rs index 4620eb3..bd7eca9 100644 --- a/src/net_prio.rs +++ b/src/net_prio.rs @@ -132,7 +132,15 @@ impl NetPrioController { self.open_path("net_prio.ifpriomap", true) .and_then(|mut file| { file.write_all(format!("{} {}", eif, prio).as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + .map_err(|e| { + Error::with_cause( + WriteFailed( + "net_prio.ifpriomap".to_string(), + format!("{} {}", eif, prio), + ), + e, + ) + }) }) } } diff --git a/src/pid.rs b/src/pid.rs index b03e3dc..9585486 100644 --- a/src/pid.rs +++ b/src/pid.rs @@ -112,7 +112,7 @@ impl PidController { }, None => Err(Error::new(ParseError)), }, - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed("pids.events".to_string()), e)), } }) } @@ -130,7 +130,7 @@ impl PidController { let res = file.read_to_string(&mut string); match res { Ok(_) => parse_max_value(&string), - Err(e) => Err(Error::with_cause(ReadFailed, e)), + Err(e) => Err(Error::with_cause(ReadFailed("pids.max".to_string()), e)), } }) } @@ -145,7 +145,10 @@ impl PidController { let string_to_write = max_pid.to_string(); match file.write_all(string_to_write.as_ref()) { Ok(_) => Ok(()), - Err(e) => Err(Error::with_cause(WriteFailed, e)), + Err(e) => Err(Error::with_cause( + WriteFailed("pids.max".to_string(), format!("{:?}", max_pid)), + e, + )), } }) } diff --git a/src/rdma.rs b/src/rdma.rs index 1ee1fc8..c053c78 100644 --- a/src/rdma.rs +++ b/src/rdma.rs @@ -84,8 +84,9 @@ impl RdmaController { /// Set a maximum usage for each RDMA/IB resource. pub fn set_max(&self, max: &str) -> Result<()> { self.open_path("rdma.max", true).and_then(|mut file| { - file.write_all(max.as_ref()) - .map_err(|e| Error::with_cause(WriteFailed, e)) + file.write_all(max.as_ref()).map_err(|e| { + Error::with_cause(WriteFailed("rdma.max".to_string(), max.to_string()), e) + }) }) } } diff --git a/tests/cpuset.rs b/tests/cpuset.rs index 6521b88..1f7b74d 100644 --- a/tests/cpuset.rs +++ b/tests/cpuset.rs @@ -83,7 +83,7 @@ fn test_cpuset_set_cpus_add_task() { println!("tasks after added: {:?}", tasks); // remove task - let _ = cg.remove_task(CgroupPid::from(pid_i)); + cg.remove_task(CgroupPid::from(pid_i)); let tasks = cg.tasks(); println!("tasks after deleted: {:?}", tasks); assert_eq!(0, tasks.len());