use crate::backend::linux::cgroup;
use crate::backend::linux::LinuxBackend;
use crate::contract::capability::Enforcement;
use crate::contract::host_control::HostControl;
use crate::contract::plan::{BoundaryPlan, BoundaryRequirement};
use crate::contract::report::{BoundaryReportBody, ObservedFact};
use std::path::Path;
pub(super) fn pids_cap_for(plan: &BoundaryPlan) -> Option<u64> {
(plan.budgets.process_count.selected_guarantee == Enforcement::Enforced)
.then_some(plan.budgets.process_count.effective_limit)
}
pub(super) fn requires_cgroup_backing(plan: &BoundaryPlan) -> bool {
pids_cap_for(plan).is_some()
|| plan.admitted.iter().any(|a| {
matches!(
a.requirement,
BoundaryRequirement::HostControl(HostControl::Kill { .. })
)
})
}
pub(super) fn create_run_leaf(base: &Path, pids_cap: Option<u64>) -> Option<cgroup::CgroupLeaf> {
use std::sync::atomic::{AtomicU64, Ordering};
static RUN_COUNTER: AtomicU64 = AtomicU64::new(0);
let suffix = RUN_COUNTER.fetch_add(1, Ordering::Relaxed);
let name = format!("bvisor-run-{}-{suffix}", std::process::id());
let limits = match pids_cap {
Some(max) => cgroup::CgroupLimits::with_pids_max(max),
None => cgroup::CgroupLimits::default(),
};
cgroup::CgroupLeaf::create(base, &name, limits).ok()
}
pub(super) fn teardown_leaf(leaf: Option<cgroup::CgroupLeaf>) -> Option<String> {
let mut leaf = leaf?;
let killed = leaf.kill();
let drained = leaf.wait_until_empty(50, std::time::Duration::from_millis(10));
let removed = leaf.remove();
if killed.is_ok() && matches!(drained, Ok(true)) && removed.is_ok() {
None
} else {
Some(format!(
"cgroup leaf teardown incomplete: kill={killed:?} drained={drained:?} removed={removed:?}"
))
}
}
pub(super) type CgroupRunPrep = (
Option<cgroup::CgroupLeaf>,
Option<std::os::fd::OwnedFd>,
Vec<ObservedFact>,
);
pub(super) fn cgroup_for_run(
backend: &LinuxBackend,
plan: &BoundaryPlan,
mut observed: Vec<ObservedFact>,
) -> Result<CgroupRunPrep, Vec<ObservedFact>> {
let leaf = backend
.cgroup_base
.as_ref()
.and_then(|base| create_run_leaf(base, pids_cap_for(plan)));
let dir_fd = leaf.as_ref().and_then(|l| l.dir_fd().ok());
if dir_fd.is_some() {
let leaf_path = leaf
.as_ref()
.and_then(|l| l.dir().ok())
.map(|d| d.display().to_string())
.unwrap_or_default();
observed.push(ObservedFact {
kind: "cgroup_leaf_prepared".to_string(),
detail: format!(
"cgroup leaf {leaf_path} created + dir fd passed to the launcher for \
CLONE_INTO_CGROUP placement; atomic run-tree teardown available (cgroup.kill)"
),
});
return Ok((leaf, dir_fd, observed));
}
if requires_cgroup_backing(plan) {
if let Some(detail) = teardown_leaf(leaf) {
observed.push(ObservedFact {
kind: "cgroup_teardown_incomplete".to_string(),
detail,
});
}
observed.push(ObservedFact {
kind: "cgroup_required_but_unavailable".to_string(),
detail: "plan admitted cgroup-backed guarantees (Enforced process_count and/or \
atomic Kill) but the per-run cgroup leaf could not be created; refusing \
to run the workload uncgrouped (fail-closed)"
.to_string(),
});
return Err(observed);
}
if backend.cgroup_base.is_some() {
observed.push(ObservedFact {
kind: "cgroup_placement_unavailable".to_string(),
detail: "cgroup base probed but the per-run leaf could not be created; the \
workload runs without cgroup placement this run"
.to_string(),
});
}
Ok((leaf, None, observed))
}
pub(super) fn finish(
leaf: Option<cgroup::CgroupLeaf>,
mut report: BoundaryReportBody,
) -> BoundaryReportBody {
if let Some(detail) = teardown_leaf(leaf) {
report.observed.push(ObservedFact {
kind: "cgroup_teardown_incomplete".to_string(),
detail,
});
}
report
}