mirror of
https://github.com/kata-containers/cgroups-rs.git
synced 2026-08-05 02:13:23 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e273cd2fb | ||
|
|
bce4b0bb3b | ||
|
|
f5a67c632e | ||
|
|
c8febbc67d | ||
|
|
2536e33696 | ||
|
|
694627e2fc |
2
.github/workflows/bvt.yaml
vendored
2
.github/workflows/bvt.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
name: BVT
|
||||
on: [pull_request]
|
||||
env:
|
||||
RUST_VERSION: 1.69.0
|
||||
RUST_VERSION: 1.85.1
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
|
||||
@@ -13,7 +13,6 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
regex = "1.1"
|
||||
nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] }
|
||||
libc = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
||||
11
src/blkio.rs
11
src/blkio.rs
@@ -181,13 +181,14 @@ fn parse_io_stat(s: String) -> Vec<IoStat> {
|
||||
|
||||
fn parse_io_service_total(s: String) -> Result<u64> {
|
||||
s.lines()
|
||||
.filter(|x| x.split_whitespace().count() == 2)
|
||||
.fold(Err(Error::new(ParseError)), |_, x| {
|
||||
match x.split_whitespace().collect::<Vec<_>>().as_slice() {
|
||||
["Total", val] => val.parse::<u64>().map_err(|_| Error::new(ParseError)),
|
||||
_ => Err(Error::new(ParseError)),
|
||||
.find_map(|line| {
|
||||
let mut parts = line.split_whitespace();
|
||||
match (parts.next(), parts.next(), parts.next()) {
|
||||
(Some("Total"), Some(val), None) => val.parse::<u64>().ok(),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::new(ParseError))
|
||||
}
|
||||
|
||||
fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> {
|
||||
|
||||
@@ -602,7 +602,7 @@ mod tests {
|
||||
"1,2,3,4".to_string(),
|
||||
"1-5,6-7,8-9".to_string(),
|
||||
];
|
||||
let expecteds = vec![
|
||||
let expecteds = [
|
||||
vec![(1, 1), (2, 2), (4, 6), (9, 9)],
|
||||
vec![],
|
||||
vec![(1, 1)],
|
||||
|
||||
@@ -295,43 +295,48 @@ impl DevicesController {
|
||||
let mut s = String::new();
|
||||
let res = file.read_to_string(&mut s);
|
||||
match res {
|
||||
Ok(_) => {
|
||||
s.lines().fold(Ok(Vec::new()), |acc, line| {
|
||||
let ls = line.split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>();
|
||||
if acc.is_err() || ls.len() != 4 {
|
||||
error!("allowed_devices: acc: {:?}, ls: {:?}", acc, ls);
|
||||
Err(Error::new(ParseError))
|
||||
} else {
|
||||
let devtype = DeviceType::from_char(ls[0].chars().next());
|
||||
let mut major = ls[1].parse::<i64>();
|
||||
let mut minor = ls[2].parse::<i64>();
|
||||
if major.is_err() && ls[1] == "*" {
|
||||
major = Ok(-1);
|
||||
}
|
||||
if minor.is_err() && ls[2] == "*" {
|
||||
minor = Ok(-1);
|
||||
}
|
||||
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(Error::new(ParseError))
|
||||
} else {
|
||||
let access = DevicePermissions::from_str(&ls[3])?;
|
||||
let mut acc = acc.unwrap();
|
||||
acc.push(DeviceResource {
|
||||
allow: true,
|
||||
devtype: devtype.unwrap(),
|
||||
major: major.unwrap(),
|
||||
minor: minor.unwrap(),
|
||||
access,
|
||||
});
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
Ok(_) => s
|
||||
.lines()
|
||||
.map(|line| parse_device_line(line, true))
|
||||
.collect(),
|
||||
Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_device_number(s: &str) -> Result<i64> {
|
||||
if s == "*" {
|
||||
Ok(-1)
|
||||
} else {
|
||||
s.parse::<i64>().map_err(|_| Error::new(ParseError))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_device_line(line: &str, allow: bool) -> Result<DeviceResource> {
|
||||
let parts: Vec<&str> = line.split([' ', ':']).collect();
|
||||
if parts.len() != 4 {
|
||||
error!("allowed_devices: invalid line format: {:?}", line);
|
||||
return Err(Error::new(ParseError));
|
||||
}
|
||||
|
||||
let devtype = DeviceType::from_char(parts[0].chars().next()).ok_or_else(|| {
|
||||
error!("allowed_devices: invalid device type: {:?}", parts[0]);
|
||||
Error::new(ParseError)
|
||||
})?;
|
||||
let major = parse_device_number(parts[1]).inspect_err(|_| {
|
||||
error!("allowed_devices: invalid major number: {:?}", parts[1]);
|
||||
})?;
|
||||
let minor = parse_device_number(parts[2]).inspect_err(|_| {
|
||||
error!("allowed_devices: invalid minor number: {:?}", parts[2]);
|
||||
})?;
|
||||
let access = DevicePermissions::from_str(parts[3])?;
|
||||
|
||||
Ok(DeviceResource {
|
||||
allow,
|
||||
devtype,
|
||||
major,
|
||||
minor,
|
||||
access,
|
||||
})
|
||||
}
|
||||
|
||||
113
src/hugetlb.rs
113
src/hugetlb.rs
@@ -181,7 +181,6 @@ impl HugeTlbController {
|
||||
}
|
||||
|
||||
pub const HUGEPAGESIZE_DIR: &str = "/sys/kernel/mm/hugepages";
|
||||
use regex::Regex;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
|
||||
@@ -263,37 +262,46 @@ pub fn get_decimal_abbrs() -> Vec<String> {
|
||||
}
|
||||
|
||||
fn parse_size(s: &str, m: &HashMap<String, u128>) -> Result<u128> {
|
||||
let re = Regex::new(r"(?P<num>\d+)(?P<mul>[kKmMgGtTpP]?)[bB]?$");
|
||||
// Remove leading/trailing whitespace.
|
||||
let s = s.trim();
|
||||
|
||||
if re.is_err() {
|
||||
// Remove an optional trailing 'b' or 'B'
|
||||
let s = if let Some(stripped) = s.strip_suffix('b').or_else(|| s.strip_suffix('B')) {
|
||||
stripped
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
// Ensure that the string is not empty after stripping.
|
||||
if s.is_empty() {
|
||||
return Err(Error::new(InvalidBytesSize));
|
||||
}
|
||||
let caps = re.unwrap().captures(s).unwrap();
|
||||
|
||||
let num = caps.name("num");
|
||||
let size: u128 = if let Some(num) = num {
|
||||
let n = num.as_str().trim().parse::<u128>();
|
||||
if n.is_err() {
|
||||
return Err(Error::new(InvalidBytesSize));
|
||||
}
|
||||
n.unwrap()
|
||||
} else {
|
||||
// The last character should be the multiplier letter.
|
||||
let last_char = s.chars().last().unwrap();
|
||||
if !"kKmMgGtTpP".contains(last_char) {
|
||||
return Err(Error::new(InvalidBytesSize));
|
||||
};
|
||||
}
|
||||
|
||||
let q = caps.name("mul");
|
||||
let mul: u128 = if let Some(q) = q {
|
||||
let t = m.get(q.as_str());
|
||||
if let Some(t) = t {
|
||||
*t
|
||||
} else {
|
||||
return Err(Error::new(InvalidBytesSize));
|
||||
}
|
||||
} else {
|
||||
// The numeric part is everything before the multiplier letter.
|
||||
let num_part = &s[..s.len() - last_char.len_utf8()];
|
||||
if num_part.trim().is_empty() {
|
||||
return Err(Error::new(InvalidBytesSize));
|
||||
};
|
||||
}
|
||||
|
||||
Ok(size * mul)
|
||||
// Parse the numeric part into a u128.
|
||||
let number: u128 = num_part
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|_| Error::new(InvalidBytesSize))?;
|
||||
|
||||
// Look up the multiplier in the provided HashMap.
|
||||
let multiplier_key = last_char.to_string();
|
||||
let multiplier = m
|
||||
.get(&multiplier_key)
|
||||
.ok_or_else(|| Error::new(InvalidBytesSize))?;
|
||||
|
||||
Ok(number * multiplier)
|
||||
}
|
||||
|
||||
fn custom_size(mut size: f64, base: f64, m: &[String]) -> String {
|
||||
@@ -305,3 +313,60 @@ fn custom_size(mut size: f64, base: f64, m: &[String]) -> String {
|
||||
|
||||
format!("{}{}", size, m[i].as_str())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_binary_size_valid() {
|
||||
let m = get_binary_size_map();
|
||||
// Valid inputs must include a multiplier letter.
|
||||
assert_eq!(parse_size("1k", &m).unwrap(), KiB);
|
||||
assert_eq!(parse_size("2m", &m).unwrap(), 2 * MiB);
|
||||
assert_eq!(parse_size("3g", &m).unwrap(), 3 * GiB);
|
||||
assert_eq!(parse_size("4t", &m).unwrap(), 4 * TiB);
|
||||
assert_eq!(parse_size("5p", &m).unwrap(), 5 * PiB);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decimal_size_valid() {
|
||||
let m = get_decimal_size_map();
|
||||
assert_eq!(parse_size("1k", &m).unwrap(), KB);
|
||||
assert_eq!(parse_size("2m", &m).unwrap(), 2 * MB);
|
||||
assert_eq!(parse_size("3g", &m).unwrap(), 3 * GB);
|
||||
assert_eq!(parse_size("4t", &m).unwrap(), 4 * TB);
|
||||
assert_eq!(parse_size("5p", &m).unwrap(), 5 * PB);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailing_b_suffix() {
|
||||
let m = get_binary_size_map();
|
||||
// Trailing 'b' or 'B' should be accepted.
|
||||
assert_eq!(parse_size("1kb", &m).unwrap(), KiB);
|
||||
assert_eq!(parse_size("2mB", &m).unwrap(), 2 * MiB);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_inputs() {
|
||||
let m = get_binary_size_map();
|
||||
// Missing multiplier letter results in error.
|
||||
assert!(parse_size("1", &m).is_err());
|
||||
// Invalid multiplier letter.
|
||||
assert!(parse_size("10x", &m).is_err());
|
||||
// Non-numeric input.
|
||||
assert!(parse_size("abc", &m).is_err());
|
||||
// Only multiplier letter with no number.
|
||||
assert!(parse_size("k", &m).is_err());
|
||||
// Number with an invalid trailing character.
|
||||
assert!(parse_size("123z", &m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uppercase_multiplier_fails() {
|
||||
let m = get_binary_size_map();
|
||||
// Although the regex matches uppercase letters, the provided map only contains lowercase keys.
|
||||
// Therefore, "1K" does not match any key and should produce an error.
|
||||
assert!(parse_size("1K", &m).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,7 +785,7 @@ impl From<u64> for CgroupPid {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a std::process::Child> for CgroupPid {
|
||||
impl From<&std::process::Child> for CgroupPid {
|
||||
fn from(u: &std::process::Child) -> CgroupPid {
|
||||
CgroupPid { pid: u.id() as u64 }
|
||||
}
|
||||
|
||||
@@ -142,9 +142,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
let hier_unevict_line = ls.next().unwrap_or_default();
|
||||
|
||||
Ok(NumaStat {
|
||||
total_pages: total_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
total_pages: total_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
total_pages_per_node: {
|
||||
@@ -157,9 +155,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
file_pages: file_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
file_pages: file_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
file_pages_per_node: {
|
||||
@@ -172,9 +168,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
anon_pages: anon_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
anon_pages: anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
anon_pages_per_node: {
|
||||
@@ -187,9 +181,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
unevictable_pages: unevict_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
unevictable_pages: unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0),
|
||||
unevictable_pages_per_node: {
|
||||
@@ -204,9 +196,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
},
|
||||
hierarchical_total_pages: {
|
||||
if !hier_total_line.is_empty() {
|
||||
hier_total_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
hier_total_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
@@ -229,9 +219,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
},
|
||||
hierarchical_file_pages: {
|
||||
if !hier_file_line.is_empty() {
|
||||
hier_file_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
hier_file_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
@@ -254,9 +242,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
},
|
||||
hierarchical_anon_pages: {
|
||||
if !hier_anon_line.is_empty() {
|
||||
hier_anon_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
hier_anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
@@ -279,9 +265,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
|
||||
},
|
||||
hierarchical_unevictable_pages: {
|
||||
if !hier_unevict_line.is_empty() {
|
||||
hier_unevict_line
|
||||
.split(|x| x == ' ' || x == '=')
|
||||
.collect::<Vec<_>>()[1]
|
||||
hier_unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
|
||||
.parse::<u64>()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
|
||||
@@ -94,36 +94,26 @@ impl NetPrioController {
|
||||
}
|
||||
|
||||
/// A map of priorities for each network interface.
|
||||
#[allow(clippy::iter_nth_zero, clippy::unnecessary_unwrap)]
|
||||
pub fn ifpriomap(&self) -> Result<HashMap<String, u64>> {
|
||||
self.open_path("net_prio.ifpriomap", false)
|
||||
.and_then(|file| {
|
||||
let bf = BufReader::new(file);
|
||||
bf.lines().fold(Ok(HashMap::new()), |acc, line| {
|
||||
if acc.is_err() {
|
||||
acc
|
||||
} else {
|
||||
let mut acc = acc.unwrap();
|
||||
let l = line.unwrap();
|
||||
let mut sp = l.split_whitespace();
|
||||
bf.lines()
|
||||
.map(|line| {
|
||||
let line = line.map_err(|_| Error::new(ParseError))?;
|
||||
let mut parts = line.split_whitespace();
|
||||
|
||||
let ifname = sp.nth(0);
|
||||
let ifprio = sp.nth(1);
|
||||
if ifname.is_none() || ifprio.is_none() {
|
||||
Err(Error::new(ParseError))
|
||||
} else {
|
||||
let ifname = ifname.unwrap();
|
||||
let ifprio = ifprio.unwrap().trim().parse();
|
||||
match ifprio {
|
||||
Err(e) => Err(Error::with_cause(ParseError, e)),
|
||||
Ok(_) => {
|
||||
acc.insert(ifname.to_string(), ifprio.unwrap());
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
let ifname = parts.next().ok_or(Error::new(ParseError))?;
|
||||
let ifprio_str = parts.next().ok_or(Error::new(ParseError))?;
|
||||
|
||||
let ifprio = ifprio_str
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|e| Error::with_cause(ParseError, e))?;
|
||||
|
||||
Ok((ifname.to_string(), ifprio))
|
||||
})
|
||||
.collect::<Result<HashMap<String, _>>>()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user