use std::path::PathBuf;
use thiserror::Error;
pub use crate::isolate::box_manager::{BoxPool, IsolateBox};
pub use crate::isolate::command::{IsolateAction, IsolateCommand};
pub use crate::isolate::meta::{MetaFile, MetaParseError};
pub use crate::isolate::process::{IsolateProcess, run_batch, run_with_output};
mod box_manager;
mod command;
mod meta;
mod process;
use crate::types::MountConfig;
pub fn prepare_cgroup(cg_root: &std::path::Path) -> Result<bool, std::io::Error> {
use std::fs;
use std::path::Path;
let cg_base = Path::new("/sys/fs/cgroup");
let controllers_path = cg_base.join("cgroup.controllers");
if !controllers_path.exists() {
return Ok(false);
}
let controllers = fs::read_to_string(&controllers_path)?;
if !controllers.split_whitespace().any(|c| c == "memory") {
return Ok(false);
}
if cg_root.exists() {
let subtree = cg_root.join("cgroup.subtree_control");
if let Ok(content) = fs::read_to_string(&subtree)
&& content.split_whitespace().any(|c| c == "memory")
{
return Ok(true);
}
}
let init_cg = cg_base.join("init");
if !init_cg.exists() {
fs::create_dir(&init_cg)?;
}
fs::write(init_cg.join("cgroup.procs"), std::process::id().to_string())?;
fs::write(cg_base.join("cgroup.subtree_control"), "+memory +pids")?;
if !cg_root.exists() {
fs::create_dir(cg_root)?;
}
fs::write(cg_root.join("cgroup.subtree_control"), "+memory +pids")?;
Ok(true)
}
#[derive(Debug, Error)]
pub enum IsolateError {
#[error("failed to initialize box {id}: {message}")]
InitFailed { id: u32, message: String },
#[error("failed to cleanup box {id}: {message}")]
CleanupFailed { id: u32, message: String },
#[error("isolate command failed: {0}")]
CommandFailed(String),
#[error("failed to spawn isolate process: {0}")]
SpawnFailed(#[source] std::io::Error),
#[error("failed to parse meta file: {0}")]
MetaParseFailed(String),
#[error("box {0} not found or not initialized")]
BoxNotFound(u32),
#[error("no available boxes in pool")]
PoolExhausted,
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("isolate binary not found at {0}")]
BinaryNotFound(PathBuf),
#[error("invalid path: {0}")]
InvalidPath(String),
#[error("mount source path does not exist: {0}")]
MountSourceNotFound(String),
}
pub fn validate_mounts(mounts: &[MountConfig]) -> Result<(), IsolateError> {
use std::path::Path;
for mount in mounts {
if mount.optional {
continue;
}
let path = Path::new(&mount.source);
if !path.exists() {
return Err(IsolateError::MountSourceNotFound(mount.source.clone()));
}
}
Ok(())
}
pub fn resolve_command(command: &mut [String]) -> Result<(), IsolateError> {
let first = match command.first_mut() {
Some(first) => first,
None => return Ok(()),
};
if first.contains('/') {
return Ok(());
}
let path_var = std::env::var("PATH").unwrap_or_default();
for dir in path_var.split(':') {
let candidate = std::path::Path::new(dir).join(&*first);
if candidate.exists() {
*first = std::fs::canonicalize(&candidate)
.unwrap_or(candidate)
.to_string_lossy()
.into_owned();
return Ok(());
}
}
Err(IsolateError::CommandFailed(format!(
"command '{}' not found in PATH",
first
)))
}