Decouple client contracts and close private role registries
This commit is contained in:
parent
786049534d
commit
22325aa76a
5 changed files with 1041 additions and 321 deletions
|
|
@ -1,7 +1,14 @@
|
|||
use std::ffi::OsString;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use lazaret_linux::process::{dispatch_private_process_role, exec_target};
|
||||
use lazaret_linux::private_role::{PrivateRole, PrivateRoleRegistry};
|
||||
use lazaret_linux::process::{PRIVATE_PROCESS_ROLE, dispatch_private_process_role, exec_target};
|
||||
|
||||
static ROLES: [PrivateRole; 1] = [PrivateRole::new(
|
||||
PRIVATE_PROCESS_ROLE,
|
||||
dispatch_private_process_role,
|
||||
)];
|
||||
static PRIVATE_ROLES: PrivateRoleRegistry = PrivateRoleRegistry::new(&ROLES);
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
|
|
@ -15,8 +22,8 @@ fn main() -> ExitCode {
|
|||
|
||||
fn run() -> std::io::Result<()> {
|
||||
let arguments = std::env::args_os().skip(1).collect::<Vec<OsString>>();
|
||||
if let Some(result) = dispatch_private_process_role(&arguments) {
|
||||
return result;
|
||||
if let Some(outcome) = PRIVATE_ROLES.dispatch(&arguments) {
|
||||
return outcome.result;
|
||||
}
|
||||
if arguments == [OsString::from("--trace-self-test")] {
|
||||
let (release, wait) = lazaret_linux::process::launch_barrier()?;
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ pub mod capability;
|
|||
pub mod filesystem;
|
||||
pub mod ipc;
|
||||
pub mod platform;
|
||||
pub mod private_role;
|
||||
pub mod process;
|
||||
|
|
|
|||
108
crates/lazaret-linux/src/private_role.rs
Normal file
108
crates/lazaret-linux/src/private_role.rs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
use std::ffi::OsString;
|
||||
use std::io;
|
||||
|
||||
pub type PrivateRoleDispatch = fn(&[OsString]) -> Option<io::Result<()>>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PrivateRole {
|
||||
name: &'static str,
|
||||
dispatch: PrivateRoleDispatch,
|
||||
}
|
||||
|
||||
impl PrivateRole {
|
||||
#[must_use]
|
||||
pub const fn new(name: &'static str, dispatch: PrivateRoleDispatch) -> Self {
|
||||
Self { name, dispatch }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn name(self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrivateRoleResult {
|
||||
pub name: &'static str,
|
||||
pub result: io::Result<()>,
|
||||
}
|
||||
|
||||
pub struct PrivateRoleRegistry {
|
||||
roles: &'static [PrivateRole],
|
||||
}
|
||||
|
||||
impl PrivateRoleRegistry {
|
||||
#[must_use]
|
||||
pub const fn new(roles: &'static [PrivateRole]) -> Self {
|
||||
Self { roles }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn dispatch(&self, arguments: &[OsString]) -> Option<PrivateRoleResult> {
|
||||
self.roles
|
||||
.iter()
|
||||
.find_map(|role| {
|
||||
(role.dispatch)(arguments).map(|result| PrivateRoleResult {
|
||||
name: role.name,
|
||||
result,
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
arguments
|
||||
.first()
|
||||
.filter(|argument| {
|
||||
argument
|
||||
.as_encoded_bytes()
|
||||
.starts_with(b"--lazaret-private-")
|
||||
})
|
||||
.map(|_| PrivateRoleResult {
|
||||
name: "unregistered-private-role",
|
||||
result: Err(io::Error::other(
|
||||
"private role is not declared by this executable",
|
||||
)),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn names(&self) -> Vec<&'static str> {
|
||||
self.roles.iter().map(|role| role.name).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn first(arguments: &[OsString]) -> Option<io::Result<()>> {
|
||||
(arguments.first() == Some(&OsString::from("--first"))).then_some(Ok(()))
|
||||
}
|
||||
|
||||
fn second(arguments: &[OsString]) -> Option<io::Result<()>> {
|
||||
(arguments.first() == Some(&OsString::from("--second"))).then_some(Ok(()))
|
||||
}
|
||||
|
||||
static ROLES: [PrivateRole; 2] = [
|
||||
PrivateRole::new("first", first),
|
||||
PrivateRole::new("second", second),
|
||||
];
|
||||
static REGISTRY: PrivateRoleRegistry = PrivateRoleRegistry::new(&ROLES);
|
||||
|
||||
#[test]
|
||||
fn registry_is_ordered_closed_and_refuses_foreign_roles() {
|
||||
assert_eq!(REGISTRY.names(), ["first", "second"]);
|
||||
assert_eq!(
|
||||
REGISTRY
|
||||
.dispatch(&[OsString::from("--second")])
|
||||
.unwrap()
|
||||
.name,
|
||||
"second"
|
||||
);
|
||||
assert!(REGISTRY.dispatch(&[]).is_none());
|
||||
assert!(REGISTRY.dispatch(&[OsString::from("--foreign")]).is_none());
|
||||
let foreign = REGISTRY
|
||||
.dispatch(&[OsString::from("--lazaret-private-foreign")])
|
||||
.unwrap();
|
||||
assert_eq!(foreign.name, "unregistered-private-role");
|
||||
assert!(foreign.result.is_err());
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ use rustix::process::{Pid, PidfdFlags, Signal, WaitId, WaitIdOptions};
|
|||
|
||||
pub const PID_FS_MAGIC: i64 = 0x5049_4446;
|
||||
const BARRIER_RELEASE: &[u8; 32] = b"LAZARET-CAUSAL-BARRIER-RELEASE!!";
|
||||
const PRIVATE_ROLE_ARGUMENT: &str = "--lazaret-private-process-role";
|
||||
pub const PRIVATE_PROCESS_ROLE: &str = "--lazaret-private-process-role";
|
||||
const WRAPPER_ROLE: &str = "launch-wrapper";
|
||||
const SIGCHLD_PROBE_ROLE: &str = "sigchld-probe";
|
||||
static CHILD_BROKER: OnceLock<ChildBroker> = OnceLock::new();
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ fn fail_running_captured<T>(
|
|||
#[allow(clippy::disallowed_types)]
|
||||
fn private_role_command(role: &str) -> std::process::Command {
|
||||
let mut command = std::process::Command::new("/proc/self/exe");
|
||||
command.arg(PRIVATE_ROLE_ARGUMENT).arg(role);
|
||||
command.arg(PRIVATE_PROCESS_ROLE).arg(role);
|
||||
command
|
||||
}
|
||||
|
||||
|
|
@ -450,7 +450,7 @@ fn private_role_command(role: &str) -> std::process::Command {
|
|||
pub fn dispatch_private_process_role(arguments: &[OsString]) -> Option<io::Result<()>> {
|
||||
if arguments
|
||||
.first()
|
||||
.is_none_or(|argument| argument != PRIVATE_ROLE_ARGUMENT)
|
||||
.is_none_or(|argument| argument != PRIVATE_PROCESS_ROLE)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue