Compare commits

...

40 Commits
v0.3.2 ... v0.3

Author SHA1 Message Date
Fabiano Fidêncio
6e273cd2fb Merge pull request #149 from kata-containers/bump-rust
Bump Rust version to 1.85.1
2025-07-09 09:37:32 +02:00
Xuewei Niu
bce4b0bb3b Fix the warning about config file
The cargo complaints that ".cargo/config" is deprecated in favor of
".cargo/config.toml". Therefore, the file is renamed to it to make cargo
happy.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 23:56:57 +08:00
Xuewei Niu
f5a67c632e Fix the issues that makes cargo clippy unhappy
Most of the issues are small, but `fold()`. The usage of `fold()` in these
cases confuses me a lot, so that I don't follow the sugguestions from
clippy to replace `fold()` with `try_fold()`. Instead, I replace them with
`map() + collect()`.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 23:56:55 +08:00
Xuewei Niu
c8febbc67d .github: Bump Rust version to 1.85.1
Rust 1.69.0, released in 2023, lags significantly behind the version used
by Kata Containers. Therefore, we bump it to 1.85.1.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-07-08 17:07:52 +08:00
Fupan Li
2536e33696 Merge pull request #143 from oguzbilgener/no-regex
refactor: Parse HugeTLB size without Regex
2025-06-12 10:23:34 +08:00
Oguz Bilgener
694627e2fc refactor: Parse HugeTLB size without Regex
This removes the only place the regex crate is used, which could lead to significant binary size savings in projects that don't use the regex craate.

Signed-off-by: Oguz Bilgener <oguz@bilgener.me>
2025-05-29 11:05:53 -04:00
Fupan Li
de9625ff57 Merge pull request #146 from fidencio/topic/release-v0.3.5
release: v0.3.5
2025-05-29 16:33:54 +08:00
Fupan Li
b6b65f79d1 Merge pull request #141 from michelhe/remove-outdated-docs
hierarchies: Remove outdated documentation
2025-05-29 16:31:55 +08:00
Fupan Li
547fb08c03 Merge pull request #140 from jokemanfire/dev
fix bug: if cgroup path has ":"
2025-05-29 16:31:14 +08:00
Fupan Li
ec9f3547ed Merge pull request #135 from zzzzzzzzzy9/pull-branch
rdma: read rdma.max file
2025-05-29 16:30:09 +08:00
Michel Heily
82a6aa491a hierarchies: Remove outdated documentation
The documentation in the hierarchies module is outdated, this project
does support cgroups2 and the unified hierarchy :)

Signed-off-by: Michel Heily <michelheily@gmail.com>
2025-05-29 10:21:27 +02:00
zzzzzzzzzy9
65c36214b7 rdma: read rdma.max file
This is a simple one, just support rdma.max.

Signed-off-by: zzzzzzzzzy9 <zhang.yu58@zte.com.cn>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-29 10:12:10 +02:00
Fabiano Fidêncio
e0d0b8f4bc release: v0.3.5
There are several fixes since the last release, and itś been a few years
waiting for a new one.

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-29 08:53:03 +02:00
Fabiano Fidêncio
db822470e5 Merge pull request #116 from xueruini/fix-mem-panic
Use default memory stats instead of panic.
2025-05-29 08:14:51 +02:00
Fabiano Fidêncio
362373b3ec memory: Fix test_procs_iterator_cgroup test
Otherwise it simply breaks.

Signed-off-by: Alex Man <alexman@stripe.com>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Ruini Xue
eadbf53140 memory: Use default memory stats instead of panic.
When containers are terminated, cgroup v2 memory metrics under
/sys/fs/cgroup may disappear.  Previously, kata-agent assumed these
metrics always exist, leading to panics as reported in
kata-containers#138.

This commit returns default value (0) when memory metric files are
missing.  This behaviour aligns with cgroup v1, which also defaults to 0
memory metric files are missing:
- Memory.limit_in_bytes which maps to m.max
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L635
- Memory.soft_limit_in_bytes which maps to m.low
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L661
- MemSwap.fail_cnt:
  https://github.com/kata-containers/cgroups-rs/blob/main/src/memory.rs#L631

Signed-off-by: Ruini Xue <ruini.xue@gmail.com>
Signed-off-by: Alex Man <alexman@stripe.com>
Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Fabiano Fidêncio
b3c57840ee lib: Ignore dead_code warning
I'm not fmiliar with this crate, so let's just ignore the warning for
now instead of removing the code.

Signed-off-by: Fabiano Fidêncio <fidencio@northflank.com>
2025-05-28 23:02:04 +02:00
Fabiano Fidêncio
b10e52d85f Merge pull request #133 from nxfs/controllers-parsing
controllers parsing is resilient to trailing newline
2025-05-28 22:44:03 +02:00
jokemanfire
7d4d4579a3 fix bug: if cgroup path has ":"
This bug is occur in cgroup path "which has :"

Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
2024-11-02 20:23:48 +08:00
Tim Zhang
eb3e37a4bc Merge pull request #132 from aa624545345/collect-cgroup-path
Correct path of cgroup when running a container in a container
2024-05-13 17:38:52 +08:00
乔琛 10307740
4005ad844d cgroup: collect path of cgroup when running a container in a container
Path of cgroup is wrong when running a container in a container. Use
the root path of mountinfo fetched from /proc/$(shim_pid)/mountinfo
to trim the path obtained from /proc/self/mountinfo.

Fixes: #131

Signed-off-by: 乔琛 10307740 <qiao.chen@zte.com.cn>
2024-04-16 16:40:05 +08:00
Fernand Sieber
ef3497646f controllers parsing is resilient to trailing newline
In older kernel versions (tested with 5.10.208), the cgroup.controllers
files ends with a newline. This newline is not trimmed and as such the
application later can incorrectly consider that the last controller is not
supported
2024-03-20 13:55:56 +02:00
Peng Tao
69ef63a0ef Merge pull request #130 from Tim-Zhang/release-0.3.4
release: v0.3.4
2023-10-25 15:35:45 +08:00
Tim Zhang
346844ca72 release: v0.3.4
To include #126, #127, #129

Signed-off-by: Tim Zhang <tim@hyper.sh>
2023-10-25 14:48:38 +08:00
Tim Zhang
4f1fe13d91 Merge pull request #127 from nrxus/cgroup2-identify-musl
make musl cgroupv2 identifying same as other target_envs
2023-10-25 14:42:03 +08:00
Andrés Medina
17a6c6b842 fix misidentification of cgroup v2 in musl targets
When compiling for a musl target, use the same CGROUP2_SUPER_MAGIC
constant that we use for other linux targets

Signed-off-by: Andrés Medina <andresnrx@gmail.com>
2023-10-24 15:52:35 -07:00
Tim Zhang
3c4b724433 Merge pull request #129 from mzweilz/mzweilz/main
Support setting max memory limit in cgroup v2
2023-10-23 20:00:55 +08:00
Tim Zhang
01885adb99 Merge pull request #126 from HeRaNO/use-peak
memory: use `peak` in `max_usage_in_bytes`
2023-10-23 20:00:25 +08:00
mengze
ce5f5f638e Support setting max memory limit in cgroup v2
When setting memory.max/memory.swap.max in cgroup v2, Linux uses "max"
instead of "-1" to indicate no restriction on memory usage. The
set_limit and set_memswap_limit functions of the memory controller
accept i64 as a parameter. In cgroupv2, if the parameter is -1, "max"
should be written into the cgroup file.

Fixes #128

Signed-off-by: mengze <mengze@linux.alibaba.com>
2023-10-23 19:14:16 +08:00
HeRaNO
be837166e9 memory: use peak in max_usage_in_bytes
Kernel 5.19 introduces `memory.peak` and kernel 6.5 introduces
`memory.swap.peak` to expose the memory and swap usage watermark.
This change use these values to fill `max_usage_in_bytes`.

Signed-off-by: HeRaNO <heran55@126.com>
2023-09-12 12:14:11 +08:00
Tim Zhang
8d29c194e3 Merge pull request #122 from justxuewei/release-033
release: v0.3.3
2023-08-03 15:18:13 +08:00
Xuewei Niu
8a82ad0ac2 release: v0.3.3
The included patches are

- 89edba0f85: gha: Bump Rust version to 1.69.0
- 66a93b1c3d: devices: Throw an error if device resources are invalid
- 55505e0b3e: Minor changes for cgroup and devices subsystem
- 0b6b229a38: add .path() method

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-08-03 15:11:31 +08:00
Bin Liu
369f3bebed Merge pull request #120 from fprasx/main
add .path() method
2023-08-03 15:10:08 +08:00
Felix Prasanna
0b6b229a38 add .path() method
Allows the user to re-retrieve the path to the cgroup.

Signed-off-by: Felix Prasanna <felix@neon.tech>
2023-08-01 10:14:45 -04:00
Tim Zhang
f55bdb1775 Merge pull request #121 from justxuewei/devices
Minor changes for cgroup and devices subsystem
2023-08-01 19:46:06 +08:00
Xuewei Niu
55505e0b3e Minor changes for cgroup and devices subsystem
The changes include:

- Expose `create()` and add `exists()` for `Cgroup`: The changes
are allowed to load cgroup and test if the cgroup exists. If not exists,
performing the `create()` directly to avoid performing `new()`.
- Make path of devices cgroup error more details: The origin path is
either `devices.allow` or `devices.deny`. It not shows which cgroup it
belongs to.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-08-01 19:36:55 +08:00
Fupan Li
df347c1db8 Merge pull request #118 from justxuewei/devices
devices: Throw an error if device resources are invalid
2023-08-01 09:51:34 +08:00
Xuewei Niu
66a93b1c3d devices: Throw an error if device resources are invalid
The cgroup-rs should throw errors while setting devices cgroup if the rule
is invalid. For example, if a cgroup has permissions of some devices. Then
we set a `a *:* rwm` to its parent's `devices.deny`. An error should be
thrown to make users realize that it is a invalid rule.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-07-31 15:18:19 +08:00
Tim Zhang
ca66292f5f Merge pull request #119 from justxuewei/ga/rust1690
gha: Bump Rust version to 1.69.0
2023-07-31 15:17:24 +08:00
Xuewei Niu
89edba0f85 gha: Bump Rust version to 1.69.0
Keep Rust version the same as kata-containers repo 's version.

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2023-07-31 15:01:17 +08:00
22 changed files with 406 additions and 271 deletions

View File

@@ -1,7 +1,7 @@
name: BVT name: BVT
on: [pull_request] on: [pull_request]
env: env:
RUST_VERSION: 1.52 RUST_VERSION: 1.85.1
jobs: jobs:
build: build:
name: Build name: Build

4
.gitignore vendored
View File

@@ -8,7 +8,3 @@ Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
/target
**/*.rs.bk
Cargo.lock

View File

@@ -5,7 +5,7 @@ repository = "https://github.com/kata-containers/cgroups-rs"
keywords = ["linux", "cgroup", "containers", "isolation"] keywords = ["linux", "cgroup", "containers", "isolation"]
categories = ["os", "api-bindings", "os::unix-apis"] categories = ["os", "api-bindings", "os::unix-apis"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.3.2" version = "0.3.5"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"] authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"]
edition = "2018" edition = "2018"
homepage = "https://github.com/kata-containers/cgroups-rs" homepage = "https://github.com/kata-containers/cgroups-rs"
@@ -13,7 +13,6 @@ readme = "README.md"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
regex = "1.1"
nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] } nix = { version = "0.25.0", default-features = false, features = ["event", "fs", "process"] }
libc = "0.2" libc = "0.2"
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }

View File

@@ -181,13 +181,14 @@ fn parse_io_stat(s: String) -> Vec<IoStat> {
fn parse_io_service_total(s: String) -> Result<u64> { fn parse_io_service_total(s: String) -> Result<u64> {
s.lines() s.lines()
.filter(|x| x.split_whitespace().count() == 2) .find_map(|line| {
.fold(Err(Error::new(ParseError)), |_, x| { let mut parts = line.split_whitespace();
match x.split_whitespace().collect::<Vec<_>>().as_slice() { match (parts.next(), parts.next(), parts.next()) {
["Total", val] => val.parse::<u64>().map_err(|_| Error::new(ParseError)), (Some("Total"), Some(val), None) => val.parse::<u64>().ok(),
_ => Err(Error::new(ParseError)), _ => None,
} }
}) })
.ok_or_else(|| Error::new(ParseError))
} }
fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> { fn parse_blkio_data(s: String) -> Result<Vec<BlkIoData>> {
@@ -430,10 +431,10 @@ impl<'a> From<&'a Subsystem> for &'a BlkIoController {
impl BlkIoController { impl BlkIoController {
/// Constructs a new `BlkIoController` with `root` serving as the root of the control group. /// Constructs a new `BlkIoController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -9,6 +9,7 @@
use crate::error::ErrorKind::*; use crate::error::ErrorKind::*;
use crate::error::*; use crate::error::*;
use crate::hierarchies::V1;
use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem}; use crate::{CgroupPid, ControllIdentifier, Controller, Hierarchy, Resources, Subsystem};
use std::collections::HashMap; use std::collections::HashMap;
@@ -73,8 +74,13 @@ impl Cgroup {
self.hier.v2() self.hier.v2()
} }
/// Return the path the cgroup is located at.
pub fn path(&self) -> &str {
&self.path
}
/// Create this control group. /// Create this control group.
fn create(&self) -> Result<()> { pub fn create(&self) -> Result<()> {
if self.hier.v2() { if self.hier.v2() {
create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers) create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers)
} else { } else {
@@ -492,6 +498,13 @@ impl Cgroup {
v.dedup(); v.dedup();
v v
} }
/// Checks if the cgroup exists.
///
/// Returns true if at least one subsystem exists.
pub fn exists(&self) -> bool {
self.subsystems().iter().any(|e| e.to_controller().exists())
}
} }
pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup"; pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup";
@@ -509,7 +522,7 @@ fn supported_controllers() -> Vec<String> {
let ret = fs::read_to_string(p.as_str()); let ret = fs::read_to_string(p.as_str());
ret.unwrap_or_default() ret.unwrap_or_default()
.split(' ') .split(' ')
.map(|x| x.to_string()) .map(|x| x.trim().to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
} }
@@ -576,19 +589,47 @@ pub fn get_cgroups_relative_paths_by_pid(pid: u32) -> Result<HashMap<String, Str
get_cgroups_relative_paths_by_path(path) get_cgroups_relative_paths_by_path(path)
} }
fn get_cgroup_destination(mut mount_root: String, pidpath: String) -> String {
if mount_root == "/" {
mount_root = String::from("");
}
pidpath.trim_start_matches(&mount_root).to_string()
}
pub fn existing_path(paths: HashMap<String, String>) -> Result<HashMap<String, String>> {
let mount_roots_v1 = V1::new();
let mut mount_roots_subsystems_map = HashMap::new();
for s in mount_roots_v1.subsystems().iter() {
let controller_name = s.controller_name();
let path_from_cgroup = paths
.get(&controller_name)
.ok_or(Error::new(Common(format!(
"controller {} found in mountinfo, but not found in cgroup.",
controller_name
))))?;
let path_from_mountinfo = s.to_controller().base().to_string_lossy().to_string();
let des_path = get_cgroup_destination(path_from_mountinfo, path_from_cgroup.to_owned());
mount_roots_subsystems_map.insert(controller_name, des_path);
}
Ok(mount_roots_subsystems_map)
}
fn get_cgroups_relative_paths_by_path(path: String) -> Result<HashMap<String, String>> { fn get_cgroups_relative_paths_by_path(path: String) -> Result<HashMap<String, String>> {
let mut m = HashMap::new(); let mut m = HashMap::new();
let content = let content =
fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?; fs::read_to_string(path.clone()).map_err(|e| Error::with_cause(ReadFailed(path), e))?;
for l in content.lines() { // cgroup path may have ":" , likes
let fl: Vec<&str> = l.split(':').collect(); // "2:cpu,cpuacct:/system.slice/containerd.service/test.slice:cri-containerd:96b37a2edf84351487f42039e137427f1812f678850675fac214caf597ee5e4a"
if fl.len() != 3 { for line in content.lines() {
continue; if let Some((first_value_part, remaining_path)) =
} line.split_once(':').unwrap_or_default().1.split_once(':')
{
let keys: Vec<&str> = fl[1].split(',').collect(); let keys: Vec<&str> = first_value_part.split(',').collect();
for key in &keys { keys.iter().for_each(|key| {
m.insert(key.to_string(), fl[2].to_string()); m.insert(key.to_string(), remaining_path.to_string());
});
} }
} }
Ok(m) Ok(m)

View File

@@ -59,7 +59,6 @@ impl ControllerInternal for CpuController {
fn get_path(&self) -> &PathBuf { fn get_path(&self) -> &PathBuf {
&self.path &self.path
} }
fn get_path_mut(&mut self) -> &mut PathBuf { fn get_path_mut(&mut self) -> &mut PathBuf {
&mut self.path &mut self.path
} }
@@ -113,10 +112,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuController {
impl CpuController { impl CpuController {
/// Contructs a new `CpuController` with `root` serving as the root of the control group. /// Contructs a new `CpuController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -100,10 +100,10 @@ impl<'a> From<&'a Subsystem> for &'a CpuAcctController {
impl CpuAcctController { impl CpuAcctController {
/// Contructs a new `CpuAcctController` with `root` serving as the root of the control group. /// Contructs a new `CpuAcctController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }

View File

@@ -254,10 +254,10 @@ fn parse_range(s: String) -> Result<Vec<(u64, u64)>> {
impl CpuSetController { impl CpuSetController {
/// Contructs a new `CpuSetController` with `root` serving as the root of the control group. /// Contructs a new `CpuSetController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }
@@ -602,7 +602,7 @@ mod tests {
"1,2,3,4".to_string(), "1,2,3,4".to_string(),
"1-5,6-7,8-9".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![(1, 1), (2, 2), (4, 6), (9, 9)],
vec![], vec![],
vec![(1, 1)], vec![(1, 1)],

View File

@@ -46,6 +46,7 @@ pub enum DeviceType {
Block, Block,
} }
#[allow(clippy::derivable_impls)]
impl Default for DeviceType { impl Default for DeviceType {
fn default() -> Self { fn default() -> Self {
DeviceType::All DeviceType::All
@@ -170,9 +171,9 @@ impl ControllerInternal for DevicesController {
for i in &res.devices { for i in &res.devices {
if i.allow { if i.allow {
let _ = self.allow_device(i.devtype, i.major, i.minor, &i.access); self.allow_device(i.devtype, i.major, i.minor, &i.access)?;
} else { } else {
let _ = self.deny_device(i.devtype, i.major, i.minor, &i.access); self.deny_device(i.devtype, i.major, i.minor, &i.access)?;
} }
} }
@@ -203,10 +204,10 @@ impl<'a> From<&'a Subsystem> for &'a DevicesController {
impl DevicesController { impl DevicesController {
/// Constructs a new `DevicesController` with `root` serving as the root of the control group. /// Constructs a new `DevicesController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
@@ -238,7 +239,13 @@ impl DevicesController {
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.allow", true).and_then(|mut file| { self.open_path("devices.allow", true).and_then(|mut file| {
file.write_all(final_str.as_ref()).map_err(|e| { file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("devices.allow".to_string(), final_str), e) Error::with_cause(
WriteFailed(
self.get_path().join("devices.allow").display().to_string(),
final_str,
),
e,
)
}) })
}) })
} }
@@ -271,7 +278,13 @@ impl DevicesController {
let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms); let final_str = format!("{} {}:{} {}", devtype.to_char(), major, minor, perms);
self.open_path("devices.deny", true).and_then(|mut file| { self.open_path("devices.deny", true).and_then(|mut file| {
file.write_all(final_str.as_ref()).map_err(|e| { file.write_all(final_str.as_ref()).map_err(|e| {
Error::with_cause(WriteFailed("devices.deny".to_string(), final_str), e) Error::with_cause(
WriteFailed(
self.get_path().join("devices.deny").display().to_string(),
final_str,
),
e,
)
}) })
}) })
} }
@@ -282,43 +295,48 @@ impl DevicesController {
let mut s = String::new(); let mut s = String::new();
let res = file.read_to_string(&mut s); let res = file.read_to_string(&mut s);
match res { match res {
Ok(_) => { Ok(_) => s
s.lines().fold(Ok(Vec::new()), |acc, line| { .lines()
let ls = line.to_string().split(|c| c == ' ' || c == ':').map(|x| x.to_string()).collect::<Vec<String>>(); .map(|line| parse_device_line(line, true))
if acc.is_err() || ls.len() != 4 { .collect(),
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)
}
}
})
},
Err(e) => Err(Error::with_cause(ReadFailed("devices.list".to_string()), e)), 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,
})
}

View File

@@ -84,14 +84,13 @@ impl<'a> From<&'a Subsystem> for &'a FreezerController {
impl FreezerController { impl FreezerController {
/// Contructs a new `FreezerController` with `root` serving as the root of the control group. /// Contructs a new `FreezerController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }
/// Freezes the processes in the control group. /// Freezes the processes in the control group.
pub fn freeze(&self) -> Result<()> { pub fn freeze(&self) -> Result<()> {
let mut file_name = "freezer.state"; let mut file_name = "freezer.state";

View File

@@ -5,9 +5,6 @@
// //
//! This module represents the various control group hierarchies the Linux kernel supports. //! This module represents the various control group hierarchies the Linux kernel supports.
//!
//! Currently, we only support the cgroupv1 hierarchy, but in the future we will add support for
//! the Unified Hierarchy.
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
@@ -37,6 +34,8 @@ use crate::cgroup::Cgroup;
/// See `proc(5)` for format details. /// See `proc(5)` for format details.
#[derive(Debug, PartialEq, Eq, Hash, Clone)] #[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Mountinfo { pub struct Mountinfo {
/// Mount root directory of the file system.
pub mount_root: PathBuf,
/// Mount pathname relative to the process's root. /// Mount pathname relative to the process's root.
pub mount_point: PathBuf, pub mount_point: PathBuf,
/// Filesystem type (main type with optional sub-type). /// Filesystem type (main type with optional sub-type).
@@ -57,6 +56,7 @@ pub(crate) fn parse_mountinfo_for_line(line: &str) -> Option<Mountinfo> {
return None; return None;
} }
let mount_point = PathBuf::from(s0_values[4]); let mount_point = PathBuf::from(s0_values[4]);
let mount_root = PathBuf::from(s0_values[3]);
let fs_type_values: Vec<_> = s1_values[0].trim().split('.').collect(); let fs_type_values: Vec<_> = s1_values[0].trim().split('.').collect();
let fs_type = match fs_type_values.len() { let fs_type = match fs_type_values.len() {
1 => (fs_type_values[0].to_string(), None), 1 => (fs_type_values[0].to_string(), None),
@@ -69,6 +69,7 @@ pub(crate) fn parse_mountinfo_for_line(line: &str) -> Option<Mountinfo> {
let super_opts: Vec<String> = s1_values[2].trim().split(',').map(String::from).collect(); let super_opts: Vec<String> = s1_values[2].trim().split(',').map(String::from).collect();
Some(Mountinfo { Some(Mountinfo {
mount_root,
mount_point, mount_point,
fs_type, fs_type,
super_opts, super_opts,
@@ -123,47 +124,53 @@ impl Hierarchy for V1 {
// The cgroup writeback feature requires cooperation between memcgs and blkcgs // The cgroup writeback feature requires cooperation between memcgs and blkcgs
// To avoid exceptions, we should add_task for blkcg before memcg(push BlkIo before Mem) // To avoid exceptions, we should add_task for blkcg before memcg(push BlkIo before Mem)
// For more Information: https://www.alibabacloud.com/help/doc-detail/155509.htm // For more Information: https://www.alibabacloud.com/help/doc-detail/155509.htm
if let Some(root) = self.get_mount_point(Controllers::BlkIo) { if let Some((point, root)) = self.get_mount_point(Controllers::BlkIo) {
subs.push(Subsystem::BlkIo(BlkIoController::new(root, false))); subs.push(Subsystem::BlkIo(BlkIoController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Mem) { if let Some((point, root)) = self.get_mount_point(Controllers::Mem) {
subs.push(Subsystem::Mem(MemController::new(root, false))); subs.push(Subsystem::Mem(MemController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Pids) { if let Some((point, root)) = self.get_mount_point(Controllers::Pids) {
subs.push(Subsystem::Pid(PidController::new(root, false))); subs.push(Subsystem::Pid(PidController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::CpuSet) { if let Some((point, root)) = self.get_mount_point(Controllers::CpuSet) {
subs.push(Subsystem::CpuSet(CpuSetController::new(root, false))); subs.push(Subsystem::CpuSet(CpuSetController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::CpuAcct) { if let Some((point, root)) = self.get_mount_point(Controllers::CpuAcct) {
subs.push(Subsystem::CpuAcct(CpuAcctController::new(root))); subs.push(Subsystem::CpuAcct(CpuAcctController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Cpu) { if let Some((point, root)) = self.get_mount_point(Controllers::Cpu) {
subs.push(Subsystem::Cpu(CpuController::new(root, false))); subs.push(Subsystem::Cpu(CpuController::new(point, root, false)));
} }
if let Some(root) = self.get_mount_point(Controllers::Devices) { if let Some((point, root)) = self.get_mount_point(Controllers::Devices) {
subs.push(Subsystem::Devices(DevicesController::new(root))); subs.push(Subsystem::Devices(DevicesController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Freezer) { if let Some((point, root)) = self.get_mount_point(Controllers::Freezer) {
subs.push(Subsystem::Freezer(FreezerController::new(root, false))); subs.push(Subsystem::Freezer(FreezerController::new(
point, root, false,
)));
} }
if let Some(root) = self.get_mount_point(Controllers::NetCls) { if let Some((point, root)) = self.get_mount_point(Controllers::NetCls) {
subs.push(Subsystem::NetCls(NetClsController::new(root))); subs.push(Subsystem::NetCls(NetClsController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::PerfEvent) { if let Some((point, root)) = self.get_mount_point(Controllers::PerfEvent) {
subs.push(Subsystem::PerfEvent(PerfEventController::new(root))); subs.push(Subsystem::PerfEvent(PerfEventController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::NetPrio) { if let Some((point, root)) = self.get_mount_point(Controllers::NetPrio) {
subs.push(Subsystem::NetPrio(NetPrioController::new(root))); subs.push(Subsystem::NetPrio(NetPrioController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::HugeTlb) { if let Some((point, root)) = self.get_mount_point(Controllers::HugeTlb) {
subs.push(Subsystem::HugeTlb(HugeTlbController::new(root, false))); subs.push(Subsystem::HugeTlb(HugeTlbController::new(
point, root, false,
)));
} }
if let Some(root) = self.get_mount_point(Controllers::Rdma) { if let Some((point, root)) = self.get_mount_point(Controllers::Rdma) {
subs.push(Subsystem::Rdma(RdmaController::new(root))); subs.push(Subsystem::Rdma(RdmaController::new(point, root)));
} }
if let Some(root) = self.get_mount_point(Controllers::Systemd) { if let Some((point, root)) = self.get_mount_point(Controllers::Systemd) {
subs.push(Subsystem::Systemd(SystemdController::new(root, false))); subs.push(Subsystem::Systemd(SystemdController::new(
point, root, false,
)));
} }
subs subs
@@ -218,29 +225,51 @@ impl Hierarchy for V2 {
for s in controller_list { for s in controller_list {
match s { match s {
"cpu" => { "cpu" => {
subs.push(Subsystem::Cpu(CpuController::new(self.root(), true))); subs.push(Subsystem::Cpu(CpuController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"io" => { "io" => {
subs.push(Subsystem::BlkIo(BlkIoController::new(self.root(), true))); subs.push(Subsystem::BlkIo(BlkIoController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"cpuset" => { "cpuset" => {
subs.push(Subsystem::CpuSet(CpuSetController::new(self.root(), true))); subs.push(Subsystem::CpuSet(CpuSetController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"memory" => { "memory" => {
subs.push(Subsystem::Mem(MemController::new(self.root(), true))); subs.push(Subsystem::Mem(MemController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"pids" => { "pids" => {
subs.push(Subsystem::Pid(PidController::new(self.root(), true))); subs.push(Subsystem::Pid(PidController::new(
self.root(),
PathBuf::from(""),
true,
)));
} }
"freezer" => { "freezer" => {
subs.push(Subsystem::Freezer(FreezerController::new( subs.push(Subsystem::Freezer(FreezerController::new(
self.root(), self.root(),
PathBuf::from(""),
true, true,
))); )));
} }
"hugetlb" => { "hugetlb" => {
subs.push(Subsystem::HugeTlb(HugeTlbController::new( subs.push(Subsystem::HugeTlb(HugeTlbController::new(
self.root(), self.root(),
PathBuf::from(""),
true, true,
))); )));
} }
@@ -275,10 +304,10 @@ impl V1 {
} }
} }
pub fn get_mount_point(&self, controller: Controllers) -> Option<PathBuf> { pub fn get_mount_point(&self, controller: Controllers) -> Option<(PathBuf, PathBuf)> {
self.mountinfo.iter().find_map(|m| { self.mountinfo.iter().find_map(|m| {
if m.fs_type.0 == "cgroup" && m.super_opts.contains(&controller.to_string()) { if m.fs_type.0 == "cgroup" && m.super_opts.contains(&controller.to_string()) {
return Some(m.mount_point.clone()); return Some((m.mount_point.to_owned(), m.mount_root.to_owned()));
} }
None None
}) })
@@ -309,43 +338,16 @@ impl Default for V2 {
pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup"; pub const UNIFIED_MOUNTPOINT: &str = "/sys/fs/cgroup";
#[cfg(any(
all(target_os = "linux", not(target_env = "musl")),
target_os = "android"
))]
pub fn is_cgroup2_unified_mode() -> bool { pub fn is_cgroup2_unified_mode() -> bool {
use nix::sys::statfs; use nix::sys::statfs;
let path = std::path::Path::new(UNIFIED_MOUNTPOINT); let path = std::path::Path::new(UNIFIED_MOUNTPOINT);
let fs_stat = statfs::statfs(path); let fs_stat = match statfs::statfs(path) {
if fs_stat.is_err() { Ok(fs_stat) => fs_stat,
return false; Err(_) => return false,
} };
// FIXME notwork, nix will not compile CGROUP2_SUPER_MAGIC because not(target_env = "musl") fs_stat.filesystem_type() == statfs::CGROUP2_SUPER_MAGIC
fs_stat.unwrap().filesystem_type() == statfs::CGROUP2_SUPER_MAGIC
}
pub const INIT_CGROUP_PATHS: &str = "/proc/1/cgroup";
#[cfg(all(target_os = "linux", target_env = "musl"))]
pub fn is_cgroup2_unified_mode() -> bool {
let lines = fs::read_to_string(INIT_CGROUP_PATHS);
if lines.is_err() {
return false;
}
for line in lines.unwrap().lines() {
let fields: Vec<&str> = line.split(':').collect();
if fields.len() != 3 {
continue;
}
if fields[0] != "0" {
return false;
}
}
true
} }
pub fn auto() -> Box<dyn Hierarchy> { pub fn auto() -> Box<dyn Hierarchy> {
@@ -364,19 +366,19 @@ mod tests {
fn test_parse_mount() { fn test_parse_mount() {
let mountinfo = vec![ let mountinfo = vec![
("29 26 0:26 / /sys/fs/cgroup/cpuset,cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,cpuset,cpu,cpuacct", ("29 26 0:26 / /sys/fs/cgroup/cpuset,cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,cpuset,cpu,cpuacct",
Mountinfo{mount_point: PathBuf::from("/sys/fs/cgroup/cpuset,cpu,cpuacct"), fs_type: ("cgroup".to_string(), None), super_opts: vec![ Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/sys/fs/cgroup/cpuset,cpu,cpuacct"), fs_type: ("cgroup".to_string(), None), super_opts: vec![
"rw".to_string(), "rw".to_string(),
"cpuset".to_string(), "cpuset".to_string(),
"cpu".to_string(), "cpu".to_string(),
"cpuacct".to_string(), "cpuacct".to_string(),
]}), ]}),
("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs shm rw,size=65536k", ("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs shm rw,size=65536k",
Mountinfo{mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), None), super_opts: vec![ Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), None), super_opts: vec![
"rw".to_string(), "rw".to_string(),
"size=65536k".to_string(), "size=65536k".to_string(),
]}), ]}),
("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs.123 shm rw,size=65536k", ("121 1731 0:42 / /shm rw,nosuid,nodev,noexec,relatime shared:68 master:66 - tmpfs.123 shm rw,size=65536k",
Mountinfo{mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), Some("123".to_string())), super_opts: vec![ Mountinfo{mount_root: PathBuf::from("/"), mount_point: PathBuf::from("/shm"), fs_type: ("tmpfs".to_string(), Some("123".to_string())), super_opts: vec![
"rw".to_string(), "rw".to_string(),
"size=65536k".to_string(), "size=65536k".to_string(),
]}), ]}),

View File

@@ -88,11 +88,11 @@ impl<'a> From<&'a Subsystem> for &'a HugeTlbController {
impl HugeTlbController { impl HugeTlbController {
/// Constructs a new `HugeTlbController` with `root` serving as the root of the control group. /// Constructs a new `HugeTlbController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
let sizes = get_hugepage_sizes(); let sizes = get_hugepage_sizes();
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
sizes, sizes,
v2, v2,
} }
@@ -181,7 +181,6 @@ impl HugeTlbController {
} }
pub const HUGEPAGESIZE_DIR: &str = "/sys/kernel/mm/hugepages"; pub const HUGEPAGESIZE_DIR: &str = "/sys/kernel/mm/hugepages";
use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; 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> { 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)); return Err(Error::new(InvalidBytesSize));
} }
let caps = re.unwrap().captures(s).unwrap();
let num = caps.name("num"); // The last character should be the multiplier letter.
let size: u128 = if let Some(num) = num { let last_char = s.chars().last().unwrap();
let n = num.as_str().trim().parse::<u128>(); if !"kKmMgGtTpP".contains(last_char) {
if n.is_err() {
return Err(Error::new(InvalidBytesSize));
}
n.unwrap()
} else {
return Err(Error::new(InvalidBytesSize)); return Err(Error::new(InvalidBytesSize));
}; }
let q = caps.name("mul"); // The numeric part is everything before the multiplier letter.
let mul: u128 = if let Some(q) = q { let num_part = &s[..s.len() - last_char.len_utf8()];
let t = m.get(q.as_str()); if num_part.trim().is_empty() {
if let Some(t) = t {
*t
} else {
return Err(Error::new(InvalidBytesSize));
}
} else {
return Err(Error::new(InvalidBytesSize)); 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 { 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()) 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());
}
}

View File

@@ -231,6 +231,7 @@ mod sealed {
}) })
} }
#[allow(dead_code)]
fn get(&self, key: &str) -> Result<String> { fn get(&self, key: &str) -> Result<String> {
self.open_path(key, false).and_then(|mut file: File| { self.open_path(key, false).and_then(|mut file: File| {
let mut string = String::new(); let mut string = String::new();
@@ -255,6 +256,9 @@ pub trait Controller {
/// The file system path to the controller. /// The file system path to the controller.
fn path(&self) -> &Path; fn path(&self) -> &Path;
/// Root path of the file system to the controller.
fn base(&self) -> &Path;
/// Apply a set of resources to the Controller, invoking its internal functions to pass the /// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information. /// kernel the information.
fn apply(&self, res: &Resources) -> Result<()>; fn apply(&self, res: &Resources) -> Result<()>;
@@ -307,6 +311,10 @@ where
self.get_path() self.get_path()
} }
fn base(&self) -> &Path {
self.get_base()
}
/// Apply a set of resources to the Controller, invoking its internal functions to pass the /// Apply a set of resources to the Controller, invoking its internal functions to pass the
/// kernel the information. /// kernel the information.
fn apply(&self, res: &Resources) -> Result<()> { fn apply(&self, res: &Resources) -> Result<()> {
@@ -777,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 { fn from(u: &std::process::Child) -> CgroupPid {
CgroupPid { pid: u.id() as u64 } CgroupPid { pid: u.id() as u64 }
} }
@@ -879,6 +887,7 @@ pub enum MaxValue {
Value(i64), Value(i64),
} }
#[allow(clippy::derivable_impls)]
impl Default for MaxValue { impl Default for MaxValue {
fn default() -> Self { fn default() -> Self {
MaxValue::Max MaxValue::Max

View File

@@ -142,9 +142,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
let hier_unevict_line = ls.next().unwrap_or_default(); let hier_unevict_line = ls.next().unwrap_or_default();
Ok(NumaStat { Ok(NumaStat {
total_pages: total_line total_pages: total_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0), .unwrap_or(0),
total_pages_per_node: { total_pages_per_node: {
@@ -157,9 +155,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}) })
.collect() .collect()
}, },
file_pages: file_line file_pages: file_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0), .unwrap_or(0),
file_pages_per_node: { file_pages_per_node: {
@@ -172,9 +168,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}) })
.collect() .collect()
}, },
anon_pages: anon_line anon_pages: anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0), .unwrap_or(0),
anon_pages_per_node: { anon_pages_per_node: {
@@ -187,9 +181,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}) })
.collect() .collect()
}, },
unevictable_pages: unevict_line unevictable_pages: unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0), .unwrap_or(0),
unevictable_pages_per_node: { unevictable_pages_per_node: {
@@ -204,9 +196,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}, },
hierarchical_total_pages: { hierarchical_total_pages: {
if !hier_total_line.is_empty() { if !hier_total_line.is_empty() {
hier_total_line hier_total_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0) .unwrap_or(0)
} else { } else {
@@ -229,9 +219,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}, },
hierarchical_file_pages: { hierarchical_file_pages: {
if !hier_file_line.is_empty() { if !hier_file_line.is_empty() {
hier_file_line hier_file_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0) .unwrap_or(0)
} else { } else {
@@ -254,9 +242,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}, },
hierarchical_anon_pages: { hierarchical_anon_pages: {
if !hier_anon_line.is_empty() { if !hier_anon_line.is_empty() {
hier_anon_line hier_anon_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0) .unwrap_or(0)
} else { } else {
@@ -279,9 +265,7 @@ fn parse_numa_stat(s: String) -> Result<NumaStat> {
}, },
hierarchical_unevictable_pages: { hierarchical_unevictable_pages: {
if !hier_unevict_line.is_empty() { if !hier_unevict_line.is_empty() {
hier_unevict_line hier_unevict_line.split([' ', '=']).collect::<Vec<_>>()[1]
.split(|x| x == ' ' || x == '=')
.collect::<Vec<_>>()[1]
.parse::<u64>() .parse::<u64>()
.unwrap_or(0) .unwrap_or(0)
} else { } else {
@@ -540,10 +524,10 @@ impl ControllerInternal for MemController {
impl MemController { impl MemController {
/// Contructs a new `MemController` with `root` serving as the root of the control group. /// Contructs a new `MemController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }
@@ -573,18 +557,33 @@ impl MemController {
// for v2 // for v2
pub fn get_mem(&self) -> Result<SetMemory> { pub fn get_mem(&self) -> Result<SetMemory> {
let mut m: SetMemory = Default::default(); let m = SetMemory {
self.get_max_value("memory.high") high: self
.map(|x| m.high = Some(x))?; .get_max_value("memory.high")
self.get_max_value("memory.low").map(|x| m.low = Some(x))?; .map_or(Some(MaxValue::default()), Some),
self.get_max_value("memory.max").map(|x| m.max = Some(x))?; low: self
self.get_max_value("memory.min").map(|x| m.min = Some(x))?; .get_max_value("memory.low")
.map_or(Some(MaxValue::Value(0)), Some),
max: self
.get_max_value("memory.max")
.map_or(Some(MaxValue::default()), Some),
min: self
.get_max_value("memory.min")
.map_or(Some(MaxValue::Value(0)), Some),
};
Ok(m) Ok(m)
} }
fn memory_stat_v2(&self) -> Memory { fn memory_stat_v2(&self) -> Memory {
let set = self.get_mem().unwrap(); // NOTE: get_mem() always returns T, but let's
// still do `unwrap_or` for safety.
let set = self.get_mem().unwrap_or(SetMemory {
low: Some(MaxValue::Value(0)),
high: Some(MaxValue::default()),
max: Some(MaxValue::default()),
min: Some(MaxValue::Value(0)),
});
Memory { Memory {
fail_cnt: 0, fail_cnt: 0,
@@ -593,7 +592,10 @@ impl MemController {
.open_path("memory.current", false) .open_path("memory.current", false)
.and_then(read_u64_from) .and_then(read_u64_from)
.unwrap_or(0), .unwrap_or(0),
max_usage_in_bytes: 0, max_usage_in_bytes: self
.open_path("memory.peak", false)
.and_then(read_u64_from)
.unwrap_or(0),
move_charge_at_immigrate: 0, move_charge_at_immigrate: 0,
numa_stat: NumaStat::default(), numa_stat: NumaStat::default(),
oom_control: OomControl::default(), oom_control: OomControl::default(),
@@ -727,7 +729,7 @@ impl MemController {
.open_path("memory.swap.events", false) .open_path("memory.swap.events", false)
.and_then(flat_keyed_to_hashmap) .and_then(flat_keyed_to_hashmap)
.map(|x| *x.get("fail").unwrap_or(&0) as u64) .map(|x| *x.get("fail").unwrap_or(&0) as u64)
.unwrap(), .unwrap_or(0),
limit_in_bytes: self limit_in_bytes: self
.open_path("memory.swap.max", false) .open_path("memory.swap.max", false)
.and_then(read_i64_from) .and_then(read_i64_from)
@@ -736,7 +738,10 @@ impl MemController {
.open_path("memory.swap.current", false) .open_path("memory.swap.current", false)
.and_then(read_u64_from) .and_then(read_u64_from)
.unwrap_or(0), .unwrap_or(0),
max_usage_in_bytes: 0, max_usage_in_bytes: self
.open_path("memory.swap.peak", false)
.and_then(read_u64_from)
.unwrap_or(0),
} }
} }
@@ -844,13 +849,16 @@ impl MemController {
/// Set the memory usage limit of the control group, in bytes. /// Set the memory usage limit of the control group, in bytes.
pub fn set_limit(&self, limit: i64) -> Result<()> { pub fn set_limit(&self, limit: i64) -> Result<()> {
let mut file_name = "memory.limit_in_bytes"; let mut file_name = "memory.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 { if self.v2 {
file_name = "memory.max"; file_name = "memory.max";
if limit == -1 {
limit_str = "max".to_string();
}
} }
self.open_path(file_name, true).and_then(|mut file| { self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| { file.write_all(limit_str.as_ref())
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}) })
} }
@@ -881,13 +889,16 @@ impl MemController {
/// Set the memory+swap limit of the control group, in bytes. /// Set the memory+swap limit of the control group, in bytes.
pub fn set_memswap_limit(&self, limit: i64) -> Result<()> { pub fn set_memswap_limit(&self, limit: i64) -> Result<()> {
let mut file_name = "memory.memsw.limit_in_bytes"; let mut file_name = "memory.memsw.limit_in_bytes";
let mut limit_str = limit.to_string();
if self.v2 { if self.v2 {
file_name = "memory.swap.max"; file_name = "memory.swap.max";
if limit == -1 {
limit_str = "max".to_string();
}
} }
self.open_path(file_name, true).and_then(|mut file| { self.open_path(file_name, true).and_then(|mut file| {
file.write_all(limit.to_string().as_ref()).map_err(|e| { file.write_all(limit_str.as_ref())
Error::with_cause(WriteFailed(file_name.to_string(), limit.to_string()), e) .map_err(|e| Error::with_cause(WriteFailed(file_name.to_string(), limit_str), e))
})
}) })
} }

View File

@@ -76,10 +76,10 @@ impl<'a> From<&'a Subsystem> for &'a NetClsController {
impl NetClsController { impl NetClsController {
/// Constructs a new `NetClsController` with `root` serving as the root of the control group. /// Constructs a new `NetClsController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }

View File

@@ -79,10 +79,10 @@ impl<'a> From<&'a Subsystem> for &'a NetPrioController {
impl NetPrioController { impl NetPrioController {
/// Constructs a new `NetPrioController` with `root` serving as the root of the control group. /// Constructs a new `NetPrioController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
@@ -94,36 +94,26 @@ impl NetPrioController {
} }
/// A map of priorities for each network interface. /// A map of priorities for each network interface.
#[allow(clippy::iter_nth_zero, clippy::unnecessary_unwrap)]
pub fn ifpriomap(&self) -> Result<HashMap<String, u64>> { pub fn ifpriomap(&self) -> Result<HashMap<String, u64>> {
self.open_path("net_prio.ifpriomap", false) self.open_path("net_prio.ifpriomap", false)
.and_then(|file| { .and_then(|file| {
let bf = BufReader::new(file); let bf = BufReader::new(file);
bf.lines().fold(Ok(HashMap::new()), |acc, line| { bf.lines()
if acc.is_err() { .map(|line| {
acc let line = line.map_err(|_| Error::new(ParseError))?;
} else { let mut parts = line.split_whitespace();
let mut acc = acc.unwrap();
let l = line.unwrap();
let mut sp = l.split_whitespace();
let ifname = sp.nth(0); let ifname = parts.next().ok_or(Error::new(ParseError))?;
let ifprio = sp.nth(1); let ifprio_str = parts.next().ok_or(Error::new(ParseError))?;
if ifname.is_none() || ifprio.is_none() {
Err(Error::new(ParseError)) let ifprio = ifprio_str
} else { .trim()
let ifname = ifname.unwrap(); .parse()
let ifprio = ifprio.unwrap().trim().parse(); .map_err(|e| Error::with_cause(ParseError, e))?;
match ifprio {
Err(e) => Err(Error::with_cause(ParseError, e)), Ok((ifname.to_string(), ifprio))
Ok(_) => { })
acc.insert(ifname.to_string(), ifprio.unwrap()); .collect::<Result<HashMap<String, _>>>()
Ok(acc)
}
}
}
}
})
}) })
} }

View File

@@ -65,10 +65,10 @@ impl<'a> From<&'a Subsystem> for &'a PerfEventController {
impl PerfEventController { impl PerfEventController {
/// Constructs a new `PerfEventController` with `root` serving as the root of the control group. /// Constructs a new `PerfEventController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
} }

View File

@@ -92,10 +92,10 @@ impl<'a> From<&'a Subsystem> for &'a PidController {
impl PidController { impl PidController {
/// Constructors a new `PidController` instance, with `root` serving as the controller's root /// Constructors a new `PidController` instance, with `root` serving as the controller's root
/// directory. /// directory.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
v2, v2,
} }
} }

View File

@@ -68,10 +68,10 @@ impl<'a> From<&'a Subsystem> for &'a RdmaController {
impl RdmaController { impl RdmaController {
/// Constructs a new `RdmaController` with `root` serving as the root of the control group. /// Constructs a new `RdmaController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf) -> Self { pub fn new(point: PathBuf, root: PathBuf) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
} }
} }
@@ -81,6 +81,11 @@ impl RdmaController {
.and_then(read_string_from) .and_then(read_string_from)
} }
/// Returns the max usage of RDMA/IB specific resources.
pub fn max(&self) -> Result<String> {
self.open_path("rdma.max", false).and_then(read_string_from)
}
/// Set a maximum usage for each RDMA/IB resource. /// Set a maximum usage for each RDMA/IB resource.
pub fn set_max(&self, max: &str) -> Result<()> { pub fn set_max(&self, max: &str) -> Result<()> {
self.open_path("rdma.max", true).and_then(|mut file| { self.open_path("rdma.max", true).and_then(|mut file| {

View File

@@ -62,10 +62,10 @@ impl<'a> From<&'a Subsystem> for &'a SystemdController {
impl SystemdController { impl SystemdController {
/// Constructs a new `SystemdController` with `root` serving as the root of the control group. /// Constructs a new `SystemdController` with `root` serving as the root of the control group.
pub fn new(root: PathBuf, v2: bool) -> Self { pub fn new(point: PathBuf, root: PathBuf, v2: bool) -> Self {
Self { Self {
base: root.clone(), base: root,
path: root, path: point,
_v2: v2, _v2: v2,
} }
} }

View File

@@ -199,7 +199,7 @@ fn test_kill_cgroup() {
} }
} }
}; };
assert!(!status.is_none()); assert!(status.is_some());
} }
cg.delete().unwrap(); cg.delete().unwrap();
} }