use std::ffi::{OsStr, OsString}; use std::fs::File; use std::io::{self, Read}; use std::os::fd::{AsFd, AsRawFd, OwnedFd}; use std::os::unix::fs::FileExt; use std::path::PathBuf; use std::time::{Duration, Instant}; use lazaret_core::codec::{decode, encode}; use lazaret_core::identity::{ ConfigurationFingerprint, CoordinatorId, DerivedPolicyDigest, DomainId, GenerationId, MountIdentity, NamespaceIdentity, NetworkSidecarId, }; use lazaret_core::network::CanonicalDerivedNetworkPolicy; use lazaret_core::records::{ HostProcessOwnershipSlot, NetworkReadinessAttestation, ProcessOwnershipCommitAck, StagedProcessOwnership, }; use lazaret_ignored_workspace::{GitSentry, PRIVATE_ROUTER_ROLE}; use lazaret_linux::capability; use lazaret_linux::filesystem::{mount_identity, sealed_memfd}; use lazaret_linux::process::{ ChildStandardIo, ParentEdge, ProcessOwnershipContext, RetainedProcess, TargetCommand, child_broker, current_process_identity, }; use lazaret_state_anchor::{ RuntimeStateAnchorAbsenceAuthority, RuntimeStateAnchorAuthority, RuntimeStateAnchorInherited, RuntimeStateAnchorMount, StateAnchorLaunchAuthority, }; use rustix::event::{PollFd, PollFlags, Timespec, poll}; use rustix::fs::{FileType, MemfdFlags, Mode, OFlags, ResolveFlags, fstat, memfd_create, openat2}; use rustix::io::{FdFlags, fcntl_setfd, read, write}; use rustix::pipe::{PipeFlags, pipe_with}; use rustix::process::Signal; use rustix::thread::CapabilitySet; use serde::{Deserialize, Serialize}; use crate::network::{ NetworkAttestationContext, NetworkProbeRequest, PreparedNetwork, enter_generation_filesystem_namespaces, enter_generation_network_namespace, prepare_network, probe_current_network, }; use crate::sandbox::RunscCommand; pub const PRIVATE_RUNTIME_ROLE: &str = "--lazaret-runtime-supervisor-role"; const MAX_CONTROL_MESSAGE: usize = 16 * 1024; const READINESS_TIMEOUT: Duration = Duration::from_secs(30); const TERMINATION_GRACE: Duration = Duration::from_secs(2); #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] pub enum AttachedRunscSignal { Stop, Continue, Kill, } impl AttachedRunscSignal { const fn signal(self) -> Signal { match self { Self::Stop => Signal::STOP, Self::Continue => Signal::CONT, Self::Kill => Signal::KILL, } } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub enum SupervisorEvent { StateAnchorStaged(StagedProcessOwnership), StateAnchorReady { staged: StagedProcessOwnership, user_namespace: lazaret_core::identity::NamespaceIdentity, mount_namespace: lazaret_core::identity::NamespaceIdentity, mount: lazaret_core::identity::MountIdentity, }, GitRoutingStaged(StagedProcessOwnership), GitRoutingReady { staged: StagedProcessOwnership, mount: MountIdentity, }, SidecarStarted, HelperStaged(StagedProcessOwnership), HelperReady(StagedProcessOwnership), NetworkAttestation(NetworkReadinessAttestation), NetworkProbe(lazaret_core::records::NetworkReachabilityProbe), HelperExited, Staged(StagedProcessOwnership), Ready { staged: StagedProcessOwnership, sentry_pid_inventory: u32, }, SignalApplied(AttachedRunscSignal), SandboxStopped, GitRoutingStopped { mount: MountIdentity, }, SidecarStopped, StateAnchorUnmounted { mount: lazaret_core::identity::MountIdentity, }, StateAnchorAbsenceObserved { user_namespace: NamespaceIdentity, mount_namespace: NamespaceIdentity, mount: MountIdentity, observed: MountIdentity, }, Exited, AttachedRunscExited, Stopped, Refused(String), } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] enum CoordinatorMessage { MountStateAnchor, StateAnchorStagedAck(ProcessOwnershipCommitAck), StartGitRouting, GitRoutingStagedAck(ProcessOwnershipCommitAck), StartSidecar, HelperStagedAck(ProcessOwnershipCommitAck), NetworkAttestationAccepted(NetworkProbeRequest), NetworkReadinessCommitted, StagedAck(ProcessOwnershipCommitAck), SandboxCommitted, SignalAttachedRunsc(AttachedRunscSignal), StopSandbox, StopGitRouting, StopSidecar, UnmountStateAnchor, ObserveStateAnchorAbsence { user_namespace: NamespaceIdentity, mount_namespace: NamespaceIdentity, mount: MountIdentity, }, Exit, Stop, } pub struct CoordinatorChannel { descriptor: OwnedFd, } impl CoordinatorChannel { #[must_use] pub const fn new(descriptor: OwnedFd) -> Self { Self { descriptor } } /// # Errors /// Returns an error for a closed, malformed, oversized, or unknown message. pub fn receive(&self) -> io::Result { receive_message(&self.descriptor) } /// # Errors /// Returns an error when exact State Anchor acquisition cannot be requested. pub fn mount_state_anchor(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::MountStateAnchor) } /// # Errors /// Returns an error when the exact daemon acknowledgment cannot be delivered. pub fn acknowledge_state_anchor( &self, acknowledgment: ProcessOwnershipCommitAck, ) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::StateAnchorStagedAck(acknowledgment), ) } /// # Errors /// Returns an error when generation-local Git routing cannot be requested. pub fn start_git_routing(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::StartGitRouting) } /// # Errors /// Returns an error when the exact router acknowledgment cannot be delivered. pub fn acknowledge_git_routing( &self, acknowledgment: ProcessOwnershipCommitAck, ) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::GitRoutingStagedAck(acknowledgment), ) } /// # Errors /// Returns an error when the RS-owned sidecar install path cannot be released. pub fn start_sidecar(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::StartSidecar) } /// # Errors /// Returns an error when the exact typed acknowledgment cannot be delivered. pub fn acknowledge_staged(&self, acknowledgment: ProcessOwnershipCommitAck) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::StagedAck(acknowledgment), ) } /// # Errors /// Returns an error when the exact helper acknowledgment cannot be delivered. pub fn acknowledge_helper(&self, acknowledgment: ProcessOwnershipCommitAck) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::HelperStagedAck(acknowledgment), ) } /// # Errors /// Returns an error when accepted attestation cannot release sandbox acquisition. pub fn acknowledge_network_attestation(&self, request: NetworkProbeRequest) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::NetworkAttestationAccepted(request), ) } /// # Errors /// Returns an error when durable network readiness cannot release sandbox acquisition. pub fn acknowledge_network_readiness(&self) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::NetworkReadinessCommitted, ) } /// # Errors /// Returns an error when the durable sandbox commit cannot be acknowledged. pub fn acknowledge_sandbox_committed(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::SandboxCommitted) } /// # Errors /// Returns an error when the exact retained-runsc action cannot be requested. pub fn signal_attached_runsc(&self, signal: AttachedRunscSignal) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::SignalAttachedRunsc(signal), ) } /// # Errors /// Returns an error when ordered sandbox stop cannot be requested. pub fn stop(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::Stop) } /// # Errors /// Returns an error when ordered sandbox stop cannot be requested. pub fn stop_sandbox(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::StopSandbox) } /// # Errors /// Returns an error when exact generation-local Git routing cannot be stopped. pub fn stop_git_routing(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::StopGitRouting) } /// # Errors /// Returns an error when ordered sidecar stop cannot be requested. pub fn stop_sidecar(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::StopSidecar) } /// # Errors /// Returns an error when exact State Anchor unmount cannot be requested. pub fn unmount_state_anchor(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::UnmountStateAnchor) } /// # Errors /// Returns an error when exact namespace-local absence observation cannot be requested. pub fn observe_state_anchor_absence( &self, user_namespace: NamespaceIdentity, mount_namespace: NamespaceIdentity, mount: MountIdentity, ) -> io::Result<()> { send_message( &self.descriptor, &CoordinatorMessage::ObserveStateAnchorAbsence { user_namespace, mount_namespace, mount, }, ) } /// # Errors /// Returns an error when graceful Runtime Supervisor exit cannot be requested. pub fn exit(&self) -> io::Result<()> { send_message(&self.descriptor, &CoordinatorMessage::Exit) } } pub struct RuntimeSupervisorLaunch { target: TargetCommand, ownership: ProcessOwnershipContext, policy_file: Option, policy_digest: Option, configuration_fingerprint: Option, network_sidecar_id: Option, runsc_command: RunscCommand, state_anchor: Option, git_routing: Option, } #[derive(Clone, Copy)] pub struct RuntimeNetworkLaunch<'policy> { pub policy: &'policy CanonicalDerivedNetworkPolicy, pub configuration_fingerprint: ConfigurationFingerprint, pub network_sidecar_id: NetworkSidecarId, } pub struct RuntimeGitRoutingLaunch { workspace: File, workspace_label: PathBuf, isolated: File, mountpoint: File, mountpoint_label: PathBuf, git_common: File, git_common_label: PathBuf, transient_hooks: File, transient_hooks_label: PathBuf, transient_config: File, transient_config_label: PathBuf, global_excludes: Option, isolate_ignored: bool, } pub struct RuntimeGitRoutingAuthority { pub workspace: File, pub workspace_label: PathBuf, pub isolated: File, pub mountpoint: File, pub mountpoint_label: PathBuf, pub git_common: File, pub git_common_label: PathBuf, pub transient_hooks: File, pub transient_hooks_label: PathBuf, pub transient_config: File, pub transient_config_label: PathBuf, pub global_excludes: Option, pub isolate_ignored: bool, } impl RuntimeGitRoutingLaunch { /// # Errors /// Refuses non-directory authorities or descriptors that cannot survive Runtime Supervisor /// launch. pub fn new(authority: RuntimeGitRoutingAuthority) -> io::Result { for authority in [ &authority.workspace, &authority.isolated, &authority.mountpoint, &authority.git_common, &authority.transient_hooks, ] { if !FileType::from_raw_mode(fstat(authority)?.st_mode).is_dir() { return Err(io::Error::other("Git-routing authority is not a directory")); } fcntl_setfd(authority, FdFlags::empty())?; } if !FileType::from_raw_mode(fstat(&authority.transient_config)?.st_mode).is_file() { return Err(io::Error::other( "Git-routing transient config authority is not a regular file", )); } fcntl_setfd(&authority.transient_config, FdFlags::empty())?; Ok(Self { workspace: authority.workspace, workspace_label: authority.workspace_label, isolated: authority.isolated, mountpoint: authority.mountpoint, mountpoint_label: authority.mountpoint_label, git_common: authority.git_common, git_common_label: authority.git_common_label, transient_hooks: authority.transient_hooks, transient_hooks_label: authority.transient_hooks_label, transient_config: authority.transient_config, transient_config_label: authority.transient_config_label, global_excludes: authority.global_excludes, isolate_ignored: authority.isolate_ignored, }) } } impl RuntimeSupervisorLaunch { /// # Errors /// Refuses any command other than the deterministic foreground attached-runsc form. pub fn from_runsc_command( ownership: ProcessOwnershipContext, command: &RunscCommand, ) -> io::Result { if ownership.slot != HostProcessOwnershipSlot::SandboxRuntimeSupervisor || command.program() != OsStr::new("runsc") || !command .arguments() .iter() .any(|argument| argument == OsStr::new("run")) || command .arguments() .iter() .any(|argument| matches!(argument.to_str(), Some("-detach" | "--detach" | "-d"))) { return Err(io::Error::other( "Runtime Supervisor requires foreground attached runsc", )); } let mut target = TargetCommand::new("/proc/self/exe") .argument(PRIVATE_RUNTIME_ROLE) .argument("--domain") .argument(hex(ownership.domain_id.as_bytes())) .argument("--generation") .argument(ownership.generation_id.get().to_string()) .argument("--coordinator") .argument(hex(ownership.coordinator_id.as_bytes())) .argument("--runsc") .argument(command.program()); for argument in command.arguments() { target = target.argument(argument); } Ok(Self { target: target.with_lifetime_pipe_parent_edge(), ownership, policy_file: None, policy_digest: None, configuration_fingerprint: None, network_sidecar_id: None, runsc_command: command.clone(), state_anchor: None, git_routing: None, }) } /// Builds the RS-owned network path with one inherited sealed policy document. /// /// # Errors /// Refuses an invalid runsc form or a policy descriptor that cannot be sealed and inherited. pub fn from_networked_runsc_command( ownership: ProcessOwnershipContext, command: &RunscCommand, network: RuntimeNetworkLaunch<'_>, ) -> io::Result { let runtime_supervisor_executable = std::env::current_exe()?; let mut launch = Self::from_runsc_command(ownership, command)?; let policy_file = sealed_memfd("lazaret-network-policy", network.policy.canonical_bytes())?; fcntl_setfd(policy_file.as_fd(), FdFlags::empty())?; let policy_fd = policy_file.as_raw_fd(); let target = std::mem::replace( &mut launch.target, TargetCommand::new("/usr/bin/false").with_lifetime_pipe_parent_edge(), ); launch.target = TargetCommand::new(runtime_supervisor_executable) .argument(PRIVATE_RUNTIME_ROLE) .argument("--domain") .argument(hex(ownership.domain_id.as_bytes())) .argument("--generation") .argument(ownership.generation_id.get().to_string()) .argument("--coordinator") .argument(hex(ownership.coordinator_id.as_bytes())) .argument("--network-policy-fd") .argument(policy_fd.to_string()) .argument("--network-policy-digest") .argument(hex(network.policy.digest().as_bytes())) .argument("--configuration-fingerprint") .argument(hex(network.configuration_fingerprint.as_bytes())) .argument("--network-sidecar") .argument(hex(network.network_sidecar_id.as_bytes())) .argument("--runsc") .argument(command.program()); for argument in command.arguments() { launch.target = launch.target.argument(argument); } launch.target = launch.target.with_lifetime_pipe_parent_edge(); drop(target); launch.policy_file = Some(policy_file); launch.policy_digest = Some(network.policy.digest()); launch.configuration_fingerprint = Some(network.configuration_fingerprint); launch.network_sidecar_id = Some(network.network_sidecar_id); Ok(launch) } /// Adds exact inherited State Anchor authorities without beginning physical acquisition. #[must_use] pub fn with_state_anchor(mut self, authority: StateAnchorLaunchAuthority) -> Self { self.state_anchor = Some(authority); self.rebuild_target(); self } #[must_use] pub fn with_git_routing(mut self, authority: RuntimeGitRoutingLaunch) -> Self { self.git_routing = Some(authority); self.rebuild_target(); self } fn rebuild_target(&mut self) { let mut target = TargetCommand::new("/proc/self/exe") .argument(PRIVATE_RUNTIME_ROLE) .argument("--domain") .argument(hex(self.ownership.domain_id.as_bytes())) .argument("--generation") .argument(self.ownership.generation_id.get().to_string()) .argument("--coordinator") .argument(hex(self.ownership.coordinator_id.as_bytes())); if let Some(authority) = &self.state_anchor { let (device, inode) = authority.expected_lower(); target = target .argument("--state-lower-fd") .argument(authority.lower_fd().to_string()) .argument("--state-lower-label") .argument(authority.lower_label()) .argument("--state-persistent-root-fd") .argument(authority.persistent_root_fd().to_string()) .argument("--state-persistent-fd") .argument(authority.persistent_anchor_fd().to_string()) .argument("--state-persistent-label") .argument(authority.persistent_anchor_label()) .argument("--state-runtime-root-fd") .argument(authority.runtime_root_fd().to_string()) .argument("--state-runtime-fd") .argument(authority.runtime_anchor_fd().to_string()) .argument("--state-runtime-label") .argument(authority.runtime_anchor_label()) .argument("--state-lower-device") .argument(device.to_string()) .argument("--state-lower-inode") .argument(inode.to_string()) .argument("--state-persistent-count") .argument(authority.persistent_destinations().len().to_string()); for destination in authority.persistent_destinations() { target = target .argument("--state-persistent-destination") .argument(destination); } } if let Some(authority) = &self.git_routing { target = target .argument("--git-workspace-fd") .argument(authority.workspace.as_raw_fd().to_string()) .argument("--git-workspace-label") .argument(&authority.workspace_label) .argument("--git-isolated-fd") .argument(authority.isolated.as_raw_fd().to_string()) .argument("--git-mountpoint-fd") .argument(authority.mountpoint.as_raw_fd().to_string()) .argument("--git-mountpoint-label") .argument(&authority.mountpoint_label) .argument("--git-common-fd") .argument(authority.git_common.as_raw_fd().to_string()) .argument("--git-common-label") .argument(&authority.git_common_label) .argument("--git-hooks-fd") .argument(authority.transient_hooks.as_raw_fd().to_string()) .argument("--git-hooks-label") .argument(&authority.transient_hooks_label) .argument("--git-config-fd") .argument(authority.transient_config.as_raw_fd().to_string()) .argument("--git-config-label") .argument(&authority.transient_config_label) .argument("--git-global-excludes") .argument( authority .global_excludes .as_deref() .unwrap_or_else(|| std::path::Path::new("")), ) .argument("--git-isolate-ignored") .argument(if authority.isolate_ignored { "true" } else { "false" }); } if let (Some(policy_file), Some(digest), Some(fingerprint), Some(sidecar)) = ( &self.policy_file, self.policy_digest, self.configuration_fingerprint, self.network_sidecar_id, ) { target = target .argument("--network-policy-fd") .argument(policy_file.as_raw_fd().to_string()) .argument("--network-policy-digest") .argument(hex(digest.as_bytes())) .argument("--configuration-fingerprint") .argument(hex(fingerprint.as_bytes())) .argument("--network-sidecar") .argument(hex(sidecar.as_bytes())); } target = target .argument("--runsc") .argument(self.runsc_command.program()); for argument in self.runsc_command.arguments() { target = target.argument(argument); } self.target = target.with_lifetime_pipe_parent_edge(); } #[must_use] pub const fn target(&self) -> &TargetCommand { &self.target } #[must_use] pub const fn ownership(&self) -> ProcessOwnershipContext { self.ownership } #[must_use] pub const fn network_sidecar_id(&self) -> Option { self.network_sidecar_id } #[must_use] pub const fn has_git_routing(&self) -> bool { self.git_routing.is_some() } #[must_use] pub const fn runsc_command(&self) -> &RunscCommand { &self.runsc_command } /// # Errors /// Refuses a Runtime Supervisor target without the lifetime-pipe parent edge. pub fn child_standard_io( &self, private_control: OwnedFd, lifetime_pipe: OwnedFd, ) -> io::Result { if self.target.parent_edge() != ParentEdge::LifetimePipe { return Err(io::Error::other( "Runtime Supervisor launch requires the lifetime-pipe parent edge", )); } Ok(ChildStandardIo::private_control_with_lifetime_pipe( private_control, lifetime_pipe, )) } } /// Runs the Runtime Supervisor's private early role before ordinary binary dispatch. /// /// # Errors /// Returns a fail-closed parse, acquisition, capability, channel, or process error. #[must_use] pub fn dispatch_private_runtime_role(arguments: &[OsString]) -> Option> { if arguments .first() .is_none_or(|argument| argument != PRIVATE_RUNTIME_ROLE) { return None; } let result = parse_runtime_arguments(&arguments[1..]).and_then(|parsed| run_supervisor(&parsed)); if let Err(error) = &result { let _ = send_message( std::io::stdout(), &SupervisorEvent::Refused(error.to_string()), ); } Some(result) } struct RuntimeArguments { ownership: ProcessOwnershipContext, state_anchor: Option, git_routing: Option, network: Option, runsc_program: OsString, runsc_arguments: Vec, } struct RuntimeGitRoutingArguments { workspace_fd: i32, workspace_label: PathBuf, isolated_fd: i32, mountpoint_fd: i32, mountpoint_label: PathBuf, git_common_fd: i32, git_common_label: PathBuf, transient_hooks_fd: i32, transient_hooks_label: PathBuf, transient_config_fd: i32, transient_config_label: PathBuf, global_excludes: Option, isolate_ignored: bool, } struct RuntimeStateAnchorArguments { lower_fd: i32, lower_label: PathBuf, persistent_root_fd: i32, persistent_anchor_fd: i32, persistent_anchor_label: PathBuf, runtime_root_fd: i32, runtime_anchor_fd: i32, runtime_anchor_label: PathBuf, expected_lower: (u64, u64), persistent_destinations: Vec, } struct RuntimeNetworkArguments { policy_fd: i32, policy_digest: DerivedPolicyDigest, configuration_fingerprint: ConfigurationFingerprint, network_sidecar_id: NetworkSidecarId, } #[allow(clippy::too_many_lines)] fn parse_runtime_arguments(arguments: &[OsString]) -> io::Result { let mut arguments = arguments.iter().peekable(); let domain = parse_fixed_hex(named(&mut arguments, "--domain")?)?; let generation = named(&mut arguments, "--generation")? .to_str() .and_then(|value| value.parse().ok()) .ok_or_else(|| io::Error::other("Runtime Supervisor generation is invalid"))?; let coordinator = parse_fixed_hex(named(&mut arguments, "--coordinator")?)?; let state_anchor = if arguments .peek() .is_some_and(|value| *value == "--state-lower-fd") { let lower_fd = parse_number(named(&mut arguments, "--state-lower-fd")?)?; let lower_label = PathBuf::from(named(&mut arguments, "--state-lower-label")?); let persistent_root_fd = parse_number(named(&mut arguments, "--state-persistent-root-fd")?)?; let persistent_anchor_fd = parse_number(named(&mut arguments, "--state-persistent-fd")?)?; let persistent_anchor_label = PathBuf::from(named(&mut arguments, "--state-persistent-label")?); let runtime_root_fd = parse_number(named(&mut arguments, "--state-runtime-root-fd")?)?; let runtime_anchor_fd = parse_number(named(&mut arguments, "--state-runtime-fd")?)?; let runtime_anchor_label = PathBuf::from(named(&mut arguments, "--state-runtime-label")?); let expected_lower = ( parse_number(named(&mut arguments, "--state-lower-device")?)?, parse_number(named(&mut arguments, "--state-lower-inode")?)?, ); let count: usize = parse_number(named(&mut arguments, "--state-persistent-count")?)?; let persistent_destinations = (0..count) .map(|_| named(&mut arguments, "--state-persistent-destination").map(PathBuf::from)) .collect::>>()?; Some(RuntimeStateAnchorArguments { lower_fd, lower_label, persistent_root_fd, persistent_anchor_fd, persistent_anchor_label, runtime_root_fd, runtime_anchor_fd, runtime_anchor_label, expected_lower, persistent_destinations, }) } else { None }; let git_routing = if arguments .peek() .is_some_and(|value| *value == "--git-workspace-fd") { let workspace_fd = parse_descriptor(named(&mut arguments, "--git-workspace-fd")?)?; let workspace_label = PathBuf::from(named(&mut arguments, "--git-workspace-label")?); let isolated_fd = parse_descriptor(named(&mut arguments, "--git-isolated-fd")?)?; let mountpoint_fd = parse_descriptor(named(&mut arguments, "--git-mountpoint-fd")?)?; let mountpoint_label = PathBuf::from(named(&mut arguments, "--git-mountpoint-label")?); let git_common_fd = parse_descriptor(named(&mut arguments, "--git-common-fd")?)?; let git_common_label = PathBuf::from(named(&mut arguments, "--git-common-label")?); let transient_hooks_fd = parse_descriptor(named(&mut arguments, "--git-hooks-fd")?)?; let transient_hooks_label = PathBuf::from(named(&mut arguments, "--git-hooks-label")?); let transient_config_fd = parse_descriptor(named(&mut arguments, "--git-config-fd")?)?; let transient_config_label = PathBuf::from(named(&mut arguments, "--git-config-label")?); let global_excludes = match named(&mut arguments, "--git-global-excludes")? { value if value.is_empty() => None, value => Some(PathBuf::from(value)), }; let isolate_ignored = match named(&mut arguments, "--git-isolate-ignored")?.to_str() { Some("true") => true, Some("false") => false, _ => { return Err(io::Error::other( "Runtime Supervisor Git isolation mode is invalid", )); } }; Some(RuntimeGitRoutingArguments { workspace_fd, workspace_label, isolated_fd, mountpoint_fd, mountpoint_label, git_common_fd, git_common_label, transient_hooks_fd, transient_hooks_label, transient_config_fd, transient_config_label, global_excludes, isolate_ignored, }) } else { None }; let network = if arguments .peek() .is_some_and(|value| *value == "--network-policy-fd") { Some(parse_network_arguments(&mut arguments)?) } else { None }; if arguments.next().is_none_or(|value| value != "--runsc") { return Err(io::Error::other( "Runtime Supervisor runsc separator is missing", )); } let program = arguments .next() .ok_or_else(|| io::Error::other("Runtime Supervisor runsc program is missing"))?; if program != "runsc" { return Err(io::Error::other("Runtime Supervisor target is not runsc")); } Ok(RuntimeArguments { ownership: ProcessOwnershipContext { domain_id: DomainId::new(domain), generation_id: GenerationId::new(generation), coordinator_id: CoordinatorId::new(coordinator), slot: HostProcessOwnershipSlot::SandboxAttachedRunsc, }, state_anchor, git_routing, network, runsc_program: (*program).clone(), runsc_arguments: arguments.cloned().collect(), }) } fn parse_number(value: &OsStr) -> io::Result where T: std::str::FromStr, { value .to_str() .and_then(|value| value.parse().ok()) .ok_or_else(|| io::Error::other("Runtime Supervisor numeric argument is invalid")) } fn parse_descriptor(value: &OsStr) -> io::Result { parse_number(value) .ok() .filter(|descriptor| *descriptor >= 0) .ok_or_else(|| io::Error::other("Runtime Supervisor descriptor is invalid")) } fn parse_network_arguments<'a>( arguments: &mut impl Iterator, ) -> io::Result { let policy_fd = named(arguments, "--network-policy-fd")? .to_str() .and_then(|value| value.parse().ok()) .filter(|value| *value >= 0) .ok_or_else(|| io::Error::other("Runtime Supervisor policy descriptor is invalid"))?; let policy_digest = DerivedPolicyDigest::new(parse_fixed_hex(named( arguments, "--network-policy-digest", )?)?); let configuration_fingerprint = ConfigurationFingerprint::new(parse_fixed_hex(named( arguments, "--configuration-fingerprint", )?)?); let network_sidecar_id = NetworkSidecarId::new(parse_fixed_hex(named(arguments, "--network-sidecar")?)?); Ok(RuntimeNetworkArguments { policy_fd, policy_digest, configuration_fingerprint, network_sidecar_id, }) } fn named<'a>( arguments: &mut impl Iterator, name: &str, ) -> io::Result<&'a OsString> { if arguments.next().is_none_or(|value| value != name) { return Err(io::Error::other(format!( "Runtime Supervisor argument {name} is missing" ))); } arguments .next() .ok_or_else(|| io::Error::other(format!("Runtime Supervisor argument {name} has no value"))) } fn parse_fixed_hex(value: &OsStr) -> io::Result<[u8; 32]> { let value = value .to_str() .ok_or_else(|| io::Error::other("Runtime Supervisor identity is not UTF-8"))?; if value.len() != 64 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Err(io::Error::other("Runtime Supervisor identity is invalid")); } let mut bytes = [0_u8; 32]; for (index, byte) in bytes.iter_mut().enumerate() { *byte = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16) .map_err(|_| io::Error::other("Runtime Supervisor identity is invalid"))?; } Ok(bytes) } include!("supervision/startup.rs"); include!("supervision/runtime.rs"); include!("supervision/tests.rs");