Compare commits

..

17 Commits

Author SHA1 Message Date
Bin Liu
7a7fd32e4f Merge pull request #168 from kata-containers/release-0.5.1
release: v0.5.1
2026-07-14 18:04:33 +08:00
Xuewei Niu
3961c4b0dd release: v0.5.1
Changes since v0.5.0:

* 7cd1cda Fix race condition in create_v2_cgroup
* 0d10296 ci: bump Rust toolchain to 1.90.0
* a32f96e build: Bump oci-spec to 0.10.0
* 3eeab4e docs: improve README formatting and badges

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2026-07-14 04:51:40 -05:00
Xuewei Niu
fdcf75b27b Merge pull request #166 from burgerdev/fix-cgroup-create-race
Fix race condition in create_v2_cgroup
2026-07-14 17:00:41 +08:00
Markus Rudy
7cd1cda42f Fix race condition in create_v2_cgroup
There used to be a race condition while creating the cgroup hierarchy:
if a parent directory was created after .exists() but before
.create_dir, the function failed unnecessarily.

This commit changes the function to use create_dir_all, which is the
atomic variant of the above pattern, and fixes some surrounding
docstrings on the way.

Signed-off-by: Markus Rudy <mr@edgeless.systems>
2026-07-13 14:31:03 +02:00
Tim Zhang
0236d210d5 Merge pull request #167 from zvonkok/bump-oci-spec
build: Bump oci-spec to 0.10.0
2026-07-10 12:02:56 +08:00
Tim Zhang
0d1029646c ci: bump Rust toolchain to 1.90.0
New zbus and zvariant releases require a newer Rust compiler.

Use Rust 1.90.0 in CI so dependency resolution can complete.

Signed-off-by: Tim Zhang <tim@hyper.sh>
2026-07-10 12:00:01 +08:00
Zvonko Kaiser
a32f96e473 build: Bump oci-spec to 0.10.0
cgroups-rs exposes oci-spec types in its public API (e.g.
Manager::set() takes &LinuxResources), so the version pinned here
dictates which oci-spec version every consumer must use: 0.x lines
are semver-incompatible, and a consumer on a newer oci-spec gets
"expected LinuxResources, found LinuxResources" type mismatches.

Kata Containers is moving its workspace to oci-spec 0.10.0 and
cannot call into cgroups-rs until this crate follows. The 0.8 to
0.10 changes are additive for everything cgroups-rs touches (no
code changes needed): the crate builds warning-free with the oci
feature and the unit tests pass (the only failures are the
pre-existing fs manager tests that need root to write cgroupfs).

Signed-off-by: Zvonko Kaiser <zkaiser@nvidia.com>
Assisted-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 21:18:34 +00:00
Tim Zhang
acad18f4a5 Merge pull request #162 from gaius-qi/feature/readme
docs: update README with badges and formatting
2026-02-03 14:38:00 +08:00
Gaius
3eeab4ef1c docs: improve README formatting and badges
- Add crates.io badge
- Simplify header and description
- Fix markdown heading levels (# -> ##)
- Fix code block language annotation spacing
- Remove redundant blank lines

Signed-off-by: Gaius <gaius.qi@gmail.com>
2026-02-02 22:26:06 +08:00
Alex Lyn
b99114bf40 Merge pull request #161 from Tim-Zhang/release-0.5.0
release: v0.5.0
2025-11-21 10:41:36 +08:00
Tim Zhang
f44ae5d4da release: v0.5.0
Bump major(minor actually) version for incompatible changes in #154.

Changelog:
- #152
- #154
- #159

Signed-off-by: Tim Zhang <tim@hyper.sh>
2025-11-21 10:31:47 +08:00
Tim Zhang
cf4c62dd34 Merge pull request #159 from Lu-yq/fix-verify-path
cgroups-rs: fix verify_path() logic
2025-11-21 10:24:26 +08:00
Lu-yq
c1a99fff50 cgroups-rs: fix verify_path() logic
remove the verify_path() logic because the
semantics change of controller.base.

Signed-off-by: Lu-yq <443471302@qq.com>
2025-11-21 01:26:26 +08:00
Xuewei Niu
6a11b32b64 Merge pull request #152 from jokemanfire/dev
fix(Controller): Errors are hidden by default
2025-11-20 19:18:36 +08:00
jokemanfire
a30729bcf1 fix(Controller): Errors are hidden by defaul
Fixed an error hidden during the creation of cgroup,
which resulted in subsequent error propagation

Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
2025-11-20 19:09:55 +08:00
Tim Zhang
69e3897a6d Merge pull request #154 from justxuewei/systemd-version
systemd: Do not check systemd version
2025-08-08 11:19:32 +08:00
Xuewei Niu
c49bee218f systemd: Do not check systemd version
The systemd version is purely informational and should not be parsed, as
documented in the [1]. In practice, systemd version has different formats
on OpenShift and Ubuntu.

This commit skips the version check and allows all operations. The errors
will be thrown from dbus when performing unsupported operations on obsolete
versions of systemd.

1: https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html

Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
2025-08-07 15:05:53 +08:00
13 changed files with 42 additions and 138 deletions

View File

@@ -1,7 +1,7 @@
name: BVT
on: [pull_request]
env:
RUST_VERSION: 1.85.1
RUST_VERSION: 1.90.0
jobs:
build:
name: Build
@@ -35,4 +35,3 @@ jobs:
- run: rustup install ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
- run: make test

View File

@@ -5,7 +5,7 @@ repository = "https://github.com/kata-containers/cgroups-rs"
keywords = ["linux", "cgroup", "containers", "isolation"]
categories = ["os", "api-bindings", "os::unix-apis"]
license = "MIT OR Apache-2.0"
version = "0.4.0"
version = "0.5.1"
authors = ["The Kata Containers community <kata-dev@lists.katacontainers.io>", "Levente Kurusa <lkurusa@acm.org>", "Sam Wilson <tecywiz121@hotmail.com>"]
edition = "2018"
homepage = "https://github.com/kata-containers/cgroups-rs"
@@ -17,7 +17,7 @@ nix = { version = "0.25.0", default-features = false, features = ["event", "fs",
libc = "0.2"
serde = { version = "1.0", features = ["derive"], optional = true }
thiserror = "1"
oci-spec = { version = "0.8.1", optional = true }
oci-spec = { version = "0.10.0", optional = true }
zbus = "5.8"
bit-vec = "0.6"

View File

@@ -1,15 +1,14 @@
# cgroups-rs ![Build](https://travis-ci.org/kata-containers/cgroups-rs.svg?branch=master)
Native Rust library for managing control groups under Linux
# cgroups-rs
Both v1 and v2 of cgroups are supported.
[![Crate](https://img.shields.io/crates/v/cgroups-rs.svg)](https://crates.io/crates/cgroups-rs)
# Examples
Native Rust library for managing Linux control groups. Supports both cgroups v1 and v2.
## Create a control group using the builder pattern
``` rust
## Examples
### Create a control group using the builder pattern
```rust
use cgroups_rs::*;
use cgroups_rs::cgroup_builder::*;
@@ -44,7 +43,7 @@ cg.delete();
// major version change.
```
# Disclaimer
## Disclaimer
This crate is licensed under:

View File

@@ -84,9 +84,9 @@ impl Cgroup {
if self.hier.v2() {
create_v2_cgroup(self.hier.root(), &self.path, &self.specified_controllers)
} else {
for subsystem in &self.subsystems {
subsystem.to_controller().create();
}
self.subsystems
.iter()
.try_for_each(|subsystem| subsystem.to_controller().create())?;
Ok(())
}
}
@@ -550,18 +550,18 @@ fn create_v2_cgroup(
// path: "a/b/c"
let elements = path.split('/').collect::<Vec<&str>>();
let last_index = elements.len() - 1;
// Build up the directory hierarchy element by element, enabling the controllers for all
// parents along the way.
for (i, ele) in elements.iter().enumerate() {
// ROOT/a
fp.push(ele);
// create dir, need not check if is a file or directory
if !fp.exists() {
if let Err(e) = std::fs::create_dir(fp.clone()) {
return Err(Error::with_cause(ErrorKind::FsError, e));
}
// create dir if necessary
if let Err(e) = std::fs::create_dir_all(fp.clone()) {
return Err(Error::with_cause(ErrorKind::FsError, e));
}
if i < last_index {
// enable controllers for substree
// enable controllers for subtree
enable_controllers(&controllers, &fp);
}
}

View File

@@ -5,7 +5,6 @@
//
#![allow(clippy::unnecessary_unwrap)]
use log::*;
use std::collections::HashMap;
use std::fmt;
@@ -165,20 +164,10 @@ mod sealed {
false
}
fn verify_path(&self) -> Result<()> {
if self.get_path().starts_with(self.get_base()) {
Ok(())
} else {
Err(Error::new(ErrorKind::InvalidPath))
}
}
fn open_path(&self, p: &str, w: bool) -> Result<File> {
let mut path = self.get_path().clone();
path.push(p);
self.verify_path()?;
if w {
match File::create(&path) {
Err(e) => Err(Error::with_cause(
@@ -214,10 +203,6 @@ mod sealed {
#[doc(hidden)]
fn path_exists(&self, p: &str) -> bool {
if self.verify_path().is_err() {
return false;
}
std::path::Path::new(p).exists()
}
}
@@ -265,7 +250,7 @@ pub trait Controller {
fn apply(&self, res: &Resources) -> Result<()>;
/// Create this controller
fn create(&self);
fn create(&self) -> Result<()>;
/// Does this controller already exist?
fn exists(&self) -> bool;
@@ -323,14 +308,11 @@ where
}
/// Create this controller
fn create(&self) {
self.verify_path()
.unwrap_or_else(|_| panic!("path should be valid: {:?}", self.path()));
match ::std::fs::create_dir_all(self.get_path()) {
Ok(_) => self.post_create(),
Err(e) => warn!("error create_dir: {:?} error: {:?}", self.get_path(), e),
}
fn create(&self) -> Result<()> {
std::fs::create_dir_all(self.get_path())
.map_err(|err| Error::with_cause(ErrorKind::FsError, err))?;
self.post_create();
Ok(())
}
/// Set notify_on_release

View File

@@ -72,22 +72,11 @@ pub mod tests {
child
}
pub fn systemd_version() -> Option<usize> {
pub fn systemd_version() -> Option<String> {
let output = Command::new("systemd").arg("--version").output().ok()?; // Return None if command execution fails
if !output.status.success() {
return None;
}
let stdout = String::from_utf8_lossy(&output.stdout);
// The first line is typically like "systemd 254 (254.5-1-arch)"
let first_line = stdout.lines().next()?;
let mut words = first_line.split_whitespace();
words.next()?; // Skip the "systemd" word
let version_str = words.next()?; // The version number as string
version_str.parse::<usize>().ok()
Some(String::from_utf8_lossy(&output.stdout).to_string())
}
}

View File

@@ -91,31 +91,21 @@ impl SystemdManager<'_> {
&self.unit
}
fn set_cpuset(
&self,
props: &mut Vec<Property>,
linux_cpu: &LinuxCpu,
systemd_version: usize,
) -> Result<()> {
fn set_cpuset(&self, props: &mut Vec<Property>, linux_cpu: &LinuxCpu) -> Result<()> {
if let Some(cpus) = linux_cpu.cpus().as_ref() {
let (id, value) = cpuset::cpus(cpus, systemd_version)?;
let (id, value) = cpuset::cpus(cpus)?;
props.push((id, value.into()));
}
if let Some(mems) = linux_cpu.mems().as_ref() {
let (id, value) = cpuset::mems(mems, systemd_version)?;
let (id, value) = cpuset::mems(mems)?;
props.push((id, value.into()));
}
Ok(())
}
fn set_cpu(
&self,
props: &mut Vec<Property>,
linux_cpu: &LinuxCpu,
systemd_version: usize,
) -> Result<()> {
fn set_cpu(&self, props: &mut Vec<Property>, linux_cpu: &LinuxCpu) -> Result<()> {
if let Some(shares) = linux_cpu.shares() {
let shares = if self.v2() {
conv::cpu_shares_to_cgroup_v2(shares)
@@ -130,7 +120,7 @@ impl SystemdManager<'_> {
let quota = linux_cpu.quota().unwrap_or(0);
if period != 0 {
let (id, value) = cpu::period(period, systemd_version)?;
let (id, value) = cpu::period(period)?;
props.push((id, value.into()));
}
@@ -272,11 +262,9 @@ impl Manager for SystemdManager<'_> {
fn set(&mut self, resources: &LinuxResources) -> Result<()> {
let mut props = vec![];
let systemd_version = self.systemd_client.systemd_version()?;
if let Some(linux_cpu) = resources.cpu() {
self.set_cpuset(&mut props, linux_cpu, systemd_version)?;
self.set_cpu(&mut props, linux_cpu, systemd_version)?;
self.set_cpuset(&mut props, linux_cpu)?;
self.set_cpu(&mut props, linux_cpu)?;
}
if let Some(linux_memory) = resources.memory() {

View File

@@ -3,10 +3,8 @@
// SPDX-License-Identifier: Apache-2.0 or MIT
//
use crate::systemd::error::{Error, Result};
use crate::systemd::{
CPU_QUOTA_PERIOD_US, CPU_QUOTA_PER_SEC_US, CPU_SHARES, CPU_SYSTEMD_VERSION, CPU_WEIGHT,
};
use crate::systemd::error::Result;
use crate::systemd::{CPU_QUOTA_PERIOD_US, CPU_QUOTA_PER_SEC_US, CPU_SHARES, CPU_WEIGHT};
/// Returns the property for CPU shares.
///
@@ -21,11 +19,7 @@ pub fn shares(shares: u64, v2: bool) -> Result<(&'static str, u64)> {
}
/// Returns the property for CPU period.
pub fn period(period: u64, systemd_version: usize) -> Result<(&'static str, u64)> {
if systemd_version < CPU_SYSTEMD_VERSION {
return Err(Error::ObsoleteSystemd);
}
pub fn period(period: u64) -> Result<(&'static str, u64)> {
Ok((CPU_QUOTA_PERIOD_US, period))
}

View File

@@ -6,29 +6,19 @@
use bit_vec::BitVec;
use crate::systemd::error::{Error, Result};
use crate::systemd::{ALLOWED_CPUS, ALLOWED_MEMORY_NODES, CPUSET_SYSTEMD_VERSION};
use crate::systemd::{ALLOWED_CPUS, ALLOWED_MEMORY_NODES};
const BYTE_IN_BITS: usize = 8;
/// Returns the property for cpuset CPUs.
pub fn cpus(cpus: &str, systemd_version: usize) -> Result<(&'static str, Vec<u8>)> {
if systemd_version < CPUSET_SYSTEMD_VERSION {
return Err(Error::ObsoleteSystemd);
}
pub fn cpus(cpus: &str) -> Result<(&'static str, Vec<u8>)> {
let mask = convert_list_to_mask(cpus)?;
Ok((ALLOWED_CPUS, mask))
}
/// Returns the property for cpuset memory nodes.
pub fn mems(mems: &str, systemd_version: usize) -> Result<(&'static str, Vec<u8>)> {
if systemd_version < CPUSET_SYSTEMD_VERSION {
return Err(Error::ObsoleteSystemd);
}
pub fn mems(mems: &str) -> Result<(&'static str, Vec<u8>)> {
let mask = convert_list_to_mask(mems)?;
Ok((ALLOWED_MEMORY_NODES, mask))
}

View File

@@ -143,21 +143,6 @@ impl SystemdClient<'_> {
Ok(())
}
/// Get the systemd version.
pub fn systemd_version(&self) -> Result<usize> {
let sys_proxy = systemd_manager_proxy()?;
// Parse 249 from "249.11-0ubuntu3.16"
let version = sys_proxy.version()?;
let version = version
.split('.')
.next()
.and_then(|v| v.parse::<usize>().ok())
.ok_or(Error::CorruptedSystemdVersion(version))?;
Ok(version)
}
/// Check if the unit exists.
pub fn exists(&self) -> bool {
let sys_proxy = match systemd_manager_proxy() {
@@ -216,7 +201,7 @@ pub mod tests {
use crate::systemd::props::PropertiesBuilder;
use crate::systemd::utils::expand_slice;
use crate::systemd::{DEFAULT_DESCRIPTION, DESCRIPTION, PIDS};
use crate::tests::{spawn_sleep_inf, spawn_yes, systemd_version};
use crate::tests::{spawn_sleep_inf, spawn_yes};
const TEST_SLICE: &str = "cgroupsrs-test.slice";
@@ -248,7 +233,7 @@ pub mod tests {
String::from_utf8_lossy(&output.stdout).to_string()
}
fn start_default_cgroup(pid: CgroupPid, unit: &str) -> SystemdClient {
fn start_default_cgroup(pid: CgroupPid, unit: &'_ str) -> SystemdClient<'_> {
let mut props = PropertiesBuilder::default_cgroup(TEST_SLICE, unit).build();
props.push((PIDS, Value::Array(vec![pid.pid as u32].into())));
let cgroup = SystemdClient::new(unit, props).unwrap();
@@ -504,19 +489,6 @@ pub mod tests {
child.wait().unwrap();
}
#[test]
fn test_systemd_version() {
skip_if_no_systemd!();
let unit = test_unit();
let props = PropertiesBuilder::default_cgroup(TEST_SLICE, &unit).build();
let cgroup = SystemdClient::new(&unit, props).unwrap();
let version = cgroup.systemd_version().unwrap();
let expected_version = systemd_version().unwrap();
assert_eq!(version, expected_version, "Systemd version mismatch");
}
#[test]
fn test_exists() {
skip_if_no_systemd!();

View File

@@ -12,7 +12,4 @@ pub enum Error {
#[error("dbus error: {0}")]
Dbus(#[from] zbus::Error),
#[error("corrupted systemd version: {0}")]
CorruptedSystemdVersion(String),
}

View File

@@ -10,9 +10,6 @@ pub enum Error {
#[error("invalid argument")]
InvalidArgument,
#[error("obsolete systemd, please upgrade your systemd")]
ObsoleteSystemd,
#[error("resource not supported by cgroups v1")]
CgroupsV1NotSupported,
}

View File

@@ -20,6 +20,3 @@ pub const DEFAULT_SLICE: &str = "system.slice";
pub const SLICE_SUFFIX: &str = ".slice";
pub const SCOPE_SUFFIX: &str = ".scope";
pub const CPU_SYSTEMD_VERSION: usize = 242;
pub const CPUSET_SYSTEMD_VERSION: usize = 244;