mod acl;
pub mod guard;
mod job;
mod locks;
use std::ffi::OsString;
use std::process::{Command, ExitCode};
use anyhow::{Context, Result};
use super::{Availability, Backend, Plan};
pub fn availability() -> Vec<(Backend, Availability)> {
let linux_only = |feature: &str| Availability::Unavailable {
reason: format!("{feature} is a Linux kernel feature with no Windows equivalent"),
};
vec![
(Backend::Mount, linux_only("mount namespaces")),
(Backend::Landlock, linux_only("Landlock")),
(
Backend::Locks,
Availability::Available {
detail: "exclusive share-mode handles, refused to every process for as \
long as Ralon holds them"
.to_string(),
},
),
]
}
pub fn enforce_and_exec(plan: &Plan, command: &[OsString]) -> Result<ExitCode> {
let Some((program, arguments)) = command.split_first() else {
anyhow::bail!("no command given");
};
let held = locks::acquire(&plan.pinned, &plan.protected)?;
let protected_directories: Vec<_> = plan
.protected
.iter()
.filter(|path| path.is_dir())
.cloned()
.collect();
let (narrowed, warnings) = acl::refuse_new_entries(&protected_directories);
for warning in &warnings {
eprintln!("ralon: warning: {warning}");
}
let mut child = Command::new(program)
.args(arguments)
.spawn()
.with_context(|| format!("failed to run `{}`", program.to_string_lossy()))?;
let leash = job::tie_to_this_process(&child);
if leash.is_none() {
eprintln!(
"ralon: warning: this command could not be tied to Ralon's lifetime, so \
killing Ralon would release the locks while the command keeps running"
);
}
let status = child
.wait()
.context("failed to wait for the command to finish")?;
drop(leash);
drop(narrowed);
drop(held);
Ok(ExitCode::from(status.code().unwrap_or(1) as u8))
}