2026-07-15 19:09:24 +09:00
|
|
|
use std::ffi::{OsStr, OsString};
|
|
|
|
|
use std::fs::File;
|
2026-07-18 02:08:14 +09:00
|
|
|
use std::io::{self, Read};
|
2026-07-15 19:09:24 +09:00
|
|
|
use std::os::fd::{AsFd, AsRawFd, OwnedFd};
|
|
|
|
|
use std::os::unix::fs::FileExt;
|
2026-07-17 03:58:27 +09:00
|
|
|
use std::path::PathBuf;
|
2026-07-15 19:09:24 +09:00
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
|
|
|
|
|
|
use lazaret_core::codec::{decode, encode};
|
2026-07-15 23:26:37 +09:00
|
|
|
use lazaret_core::identity::{
|
|
|
|
|
ConfigurationFingerprint, CoordinatorId, DerivedPolicyDigest, DomainId, GenerationId,
|
2026-07-17 04:40:02 +09:00
|
|
|
MountIdentity, NamespaceIdentity, NetworkSidecarId,
|
2026-07-15 23:26:37 +09:00
|
|
|
};
|
|
|
|
|
use lazaret_core::network::CanonicalDerivedNetworkPolicy;
|
2026-07-15 19:09:24 +09:00
|
|
|
use lazaret_core::records::{
|
2026-07-15 23:26:37 +09:00
|
|
|
HostProcessOwnershipSlot, NetworkReadinessAttestation, ProcessOwnershipCommitAck,
|
|
|
|
|
StagedProcessOwnership,
|
2026-07-15 19:09:24 +09:00
|
|
|
};
|
2026-07-18 02:08:14 +09:00
|
|
|
use lazaret_ignored_workspace::{GitSentry, PRIVATE_ROUTER_ROLE};
|
2026-07-15 19:09:24 +09:00
|
|
|
use lazaret_linux::capability;
|
2026-07-18 02:08:14 +09:00
|
|
|
use lazaret_linux::filesystem::{mount_identity, sealed_memfd};
|
2026-07-15 19:09:24 +09:00
|
|
|
use lazaret_linux::process::{
|
2026-07-15 19:25:50 +09:00
|
|
|
ChildStandardIo, ParentEdge, ProcessOwnershipContext, RetainedProcess, TargetCommand,
|
|
|
|
|
child_broker, current_process_identity,
|
2026-07-15 19:09:24 +09:00
|
|
|
};
|
2026-07-16 17:29:23 +09:00
|
|
|
use lazaret_state_anchor::{
|
2026-07-17 04:40:02 +09:00
|
|
|
RuntimeStateAnchorAbsenceAuthority, RuntimeStateAnchorAuthority, RuntimeStateAnchorInherited,
|
|
|
|
|
RuntimeStateAnchorMount, StateAnchorLaunchAuthority,
|
2026-07-16 17:29:23 +09:00
|
|
|
};
|
2026-07-15 19:09:24 +09:00
|
|
|
use rustix::event::{PollFd, PollFlags, Timespec, poll};
|
2026-07-18 02:08:14 +09:00
|
|
|
use rustix::fs::{FileType, MemfdFlags, Mode, OFlags, ResolveFlags, fstat, memfd_create, openat2};
|
2026-07-15 19:09:24 +09:00
|
|
|
use rustix::io::{FdFlags, fcntl_setfd, read, write};
|
2026-07-18 02:08:14 +09:00
|
|
|
use rustix::pipe::{PipeFlags, pipe_with};
|
2026-07-15 19:09:24 +09:00
|
|
|
use rustix::process::Signal;
|
|
|
|
|
use rustix::thread::CapabilitySet;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
use crate::network::{
|
2026-07-17 03:58:27 +09:00
|
|
|
NetworkAttestationContext, NetworkProbeRequest, PreparedNetwork,
|
2026-07-17 14:36:40 +09:00
|
|
|
enter_generation_filesystem_namespaces, enter_generation_network_namespace, prepare_network,
|
|
|
|
|
probe_current_network,
|
2026-07-15 23:26:37 +09:00
|
|
|
};
|
2026-07-15 19:09:24 +09:00
|
|
|
use crate::sandbox::RunscCommand;
|
|
|
|
|
|
2026-07-19 03:44:30 +09:00
|
|
|
pub const PRIVATE_RUNTIME_ROLE: &str = "--lazaret-runtime-supervisor-role";
|
2026-07-15 19:09:24 +09:00
|
|
|
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 {
|
2026-07-16 17:29:23 +09:00
|
|
|
StateAnchorStaged(StagedProcessOwnership),
|
|
|
|
|
StateAnchorReady {
|
|
|
|
|
staged: StagedProcessOwnership,
|
2026-07-17 03:58:27 +09:00
|
|
|
user_namespace: lazaret_core::identity::NamespaceIdentity,
|
|
|
|
|
mount_namespace: lazaret_core::identity::NamespaceIdentity,
|
2026-07-16 17:29:23 +09:00
|
|
|
mount: lazaret_core::identity::MountIdentity,
|
|
|
|
|
},
|
2026-07-18 02:08:14 +09:00
|
|
|
GitRoutingStaged(StagedProcessOwnership),
|
|
|
|
|
GitRoutingReady {
|
|
|
|
|
staged: StagedProcessOwnership,
|
|
|
|
|
mount: MountIdentity,
|
|
|
|
|
},
|
2026-07-16 17:29:23 +09:00
|
|
|
SidecarStarted,
|
2026-07-15 23:26:37 +09:00
|
|
|
HelperStaged(StagedProcessOwnership),
|
|
|
|
|
HelperReady(StagedProcessOwnership),
|
|
|
|
|
NetworkAttestation(NetworkReadinessAttestation),
|
|
|
|
|
NetworkProbe(lazaret_core::records::NetworkReachabilityProbe),
|
|
|
|
|
HelperExited,
|
2026-07-15 19:09:24 +09:00
|
|
|
Staged(StagedProcessOwnership),
|
|
|
|
|
Ready {
|
|
|
|
|
staged: StagedProcessOwnership,
|
|
|
|
|
sentry_pid_inventory: u32,
|
|
|
|
|
},
|
|
|
|
|
SignalApplied(AttachedRunscSignal),
|
2026-07-16 17:29:23 +09:00
|
|
|
SandboxStopped,
|
2026-07-18 02:08:14 +09:00
|
|
|
GitRoutingStopped {
|
|
|
|
|
mount: MountIdentity,
|
|
|
|
|
},
|
2026-07-16 17:29:23 +09:00
|
|
|
SidecarStopped,
|
2026-07-17 03:58:27 +09:00
|
|
|
StateAnchorUnmounted {
|
|
|
|
|
mount: lazaret_core::identity::MountIdentity,
|
|
|
|
|
},
|
2026-07-17 04:40:02 +09:00
|
|
|
StateAnchorAbsenceObserved {
|
|
|
|
|
user_namespace: NamespaceIdentity,
|
|
|
|
|
mount_namespace: NamespaceIdentity,
|
|
|
|
|
mount: MountIdentity,
|
|
|
|
|
observed: MountIdentity,
|
|
|
|
|
},
|
2026-07-16 17:29:23 +09:00
|
|
|
Exited,
|
2026-07-15 19:09:24 +09:00
|
|
|
AttachedRunscExited,
|
|
|
|
|
Stopped,
|
|
|
|
|
Refused(String),
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
2026-07-15 19:09:24 +09:00
|
|
|
enum CoordinatorMessage {
|
2026-07-16 17:29:23 +09:00
|
|
|
MountStateAnchor,
|
|
|
|
|
StateAnchorStagedAck(ProcessOwnershipCommitAck),
|
2026-07-18 02:08:14 +09:00
|
|
|
StartGitRouting,
|
|
|
|
|
GitRoutingStagedAck(ProcessOwnershipCommitAck),
|
2026-07-16 17:29:23 +09:00
|
|
|
StartSidecar,
|
2026-07-15 23:26:37 +09:00
|
|
|
HelperStagedAck(ProcessOwnershipCommitAck),
|
|
|
|
|
NetworkAttestationAccepted(NetworkProbeRequest),
|
|
|
|
|
NetworkReadinessCommitted,
|
2026-07-15 19:09:24 +09:00
|
|
|
StagedAck(ProcessOwnershipCommitAck),
|
|
|
|
|
SandboxCommitted,
|
|
|
|
|
SignalAttachedRunsc(AttachedRunscSignal),
|
2026-07-16 17:29:23 +09:00
|
|
|
StopSandbox,
|
2026-07-18 02:08:14 +09:00
|
|
|
StopGitRouting,
|
2026-07-16 17:29:23 +09:00
|
|
|
StopSidecar,
|
|
|
|
|
UnmountStateAnchor,
|
2026-07-17 04:40:02 +09:00
|
|
|
ObserveStateAnchorAbsence {
|
|
|
|
|
user_namespace: NamespaceIdentity,
|
|
|
|
|
mount_namespace: NamespaceIdentity,
|
|
|
|
|
mount: MountIdentity,
|
|
|
|
|
},
|
2026-07-16 17:29:23 +09:00
|
|
|
Exit,
|
2026-07-15 19:09:24 +09:00
|
|
|
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<SupervisorEvent> {
|
|
|
|
|
receive_message(&self.descriptor)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
/// # 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),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
/// # 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),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
/// # 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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:09:24 +09:00
|
|
|
/// # 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),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
/// # 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,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:09:24 +09:00
|
|
|
/// # 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)
|
|
|
|
|
}
|
2026-07-16 17:29:23 +09:00
|
|
|
|
|
|
|
|
/// # Errors
|
|
|
|
|
/// Returns an error when ordered sandbox stop cannot be requested.
|
|
|
|
|
pub fn stop_sandbox(&self) -> io::Result<()> {
|
|
|
|
|
send_message(&self.descriptor, &CoordinatorMessage::StopSandbox)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
/// # 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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
/// # 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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-17 04:40:02 +09:00
|
|
|
/// # 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,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
/// # Errors
|
|
|
|
|
/// Returns an error when graceful Runtime Supervisor exit cannot be requested.
|
|
|
|
|
pub fn exit(&self) -> io::Result<()> {
|
|
|
|
|
send_message(&self.descriptor, &CoordinatorMessage::Exit)
|
|
|
|
|
}
|
2026-07-15 19:09:24 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct RuntimeSupervisorLaunch {
|
|
|
|
|
target: TargetCommand,
|
|
|
|
|
ownership: ProcessOwnershipContext,
|
2026-07-16 15:20:13 +09:00
|
|
|
policy_file: Option<File>,
|
2026-07-16 17:29:23 +09:00
|
|
|
policy_digest: Option<DerivedPolicyDigest>,
|
|
|
|
|
configuration_fingerprint: Option<ConfigurationFingerprint>,
|
2026-07-15 23:26:37 +09:00
|
|
|
network_sidecar_id: Option<NetworkSidecarId>,
|
2026-07-16 15:20:13 +09:00
|
|
|
runsc_command: RunscCommand,
|
2026-07-16 17:29:23 +09:00
|
|
|
state_anchor: Option<StateAnchorLaunchAuthority>,
|
2026-07-18 02:08:14 +09:00
|
|
|
git_routing: Option<RuntimeGitRoutingLaunch>,
|
2026-07-15 23:26:37 +09:00
|
|
|
}
|
|
|
|
|
|
2026-07-16 15:20:13 +09:00
|
|
|
#[derive(Clone, Copy)]
|
2026-07-15 23:26:37 +09:00
|
|
|
pub struct RuntimeNetworkLaunch<'policy> {
|
|
|
|
|
pub policy: &'policy CanonicalDerivedNetworkPolicy,
|
|
|
|
|
pub configuration_fingerprint: ConfigurationFingerprint,
|
|
|
|
|
pub network_sidecar_id: NetworkSidecarId,
|
2026-07-15 19:09:24 +09:00
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
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<PathBuf>,
|
|
|
|
|
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<PathBuf>,
|
|
|
|
|
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<Self> {
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:09:24 +09:00
|
|
|
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<Self> {
|
|
|
|
|
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")
|
2026-07-19 03:44:30 +09:00
|
|
|
.argument(PRIVATE_RUNTIME_ROLE)
|
2026-07-15 19:09:24 +09:00
|
|
|
.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,
|
2026-07-16 15:20:13 +09:00
|
|
|
policy_file: None,
|
2026-07-16 17:29:23 +09:00
|
|
|
policy_digest: None,
|
|
|
|
|
configuration_fingerprint: None,
|
2026-07-15 23:26:37 +09:00
|
|
|
network_sidecar_id: None,
|
2026-07-16 15:20:13 +09:00
|
|
|
runsc_command: command.clone(),
|
2026-07-16 17:29:23 +09:00
|
|
|
state_anchor: None,
|
2026-07-18 02:08:14 +09:00
|
|
|
git_routing: None,
|
2026-07-15 19:09:24 +09:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
/// 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<Self> {
|
|
|
|
|
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)
|
2026-07-19 03:44:30 +09:00
|
|
|
.argument(PRIVATE_RUNTIME_ROLE)
|
2026-07-15 23:26:37 +09:00
|
|
|
.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);
|
2026-07-16 15:20:13 +09:00
|
|
|
launch.policy_file = Some(policy_file);
|
2026-07-16 17:29:23 +09:00
|
|
|
launch.policy_digest = Some(network.policy.digest());
|
|
|
|
|
launch.configuration_fingerprint = Some(network.configuration_fingerprint);
|
2026-07-15 23:26:37 +09:00
|
|
|
launch.network_sidecar_id = Some(network.network_sidecar_id);
|
|
|
|
|
Ok(launch)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
/// 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
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
#[must_use]
|
|
|
|
|
pub fn with_git_routing(mut self, authority: RuntimeGitRoutingLaunch) -> Self {
|
|
|
|
|
self.git_routing = Some(authority);
|
|
|
|
|
self.rebuild_target();
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
fn rebuild_target(&mut self) {
|
|
|
|
|
let mut target = TargetCommand::new("/proc/self/exe")
|
2026-07-19 03:44:30 +09:00
|
|
|
.argument(PRIVATE_RUNTIME_ROLE)
|
2026-07-16 17:29:23 +09:00
|
|
|
.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())
|
2026-07-17 03:58:27 +09:00
|
|
|
.argument("--state-lower-label")
|
|
|
|
|
.argument(authority.lower_label())
|
|
|
|
|
.argument("--state-persistent-root-fd")
|
|
|
|
|
.argument(authority.persistent_root_fd().to_string())
|
2026-07-16 17:29:23 +09:00
|
|
|
.argument("--state-persistent-fd")
|
|
|
|
|
.argument(authority.persistent_anchor_fd().to_string())
|
2026-07-17 03:58:27 +09:00
|
|
|
.argument("--state-persistent-label")
|
|
|
|
|
.argument(authority.persistent_anchor_label())
|
|
|
|
|
.argument("--state-runtime-root-fd")
|
|
|
|
|
.argument(authority.runtime_root_fd().to_string())
|
2026-07-16 17:29:23 +09:00
|
|
|
.argument("--state-runtime-fd")
|
|
|
|
|
.argument(authority.runtime_anchor_fd().to_string())
|
2026-07-17 03:58:27 +09:00
|
|
|
.argument("--state-runtime-label")
|
|
|
|
|
.argument(authority.runtime_anchor_label())
|
2026-07-16 17:29:23 +09:00
|
|
|
.argument("--state-lower-device")
|
|
|
|
|
.argument(device.to_string())
|
|
|
|
|
.argument("--state-lower-inode")
|
2026-07-17 03:58:27 +09:00
|
|
|
.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);
|
|
|
|
|
}
|
2026-07-16 17:29:23 +09:00
|
|
|
}
|
2026-07-18 02:08:14 +09:00
|
|
|
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"
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-07-16 17:29:23 +09:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:09:24 +09:00
|
|
|
#[must_use]
|
|
|
|
|
pub const fn target(&self) -> &TargetCommand {
|
|
|
|
|
&self.target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub const fn ownership(&self) -> ProcessOwnershipContext {
|
|
|
|
|
self.ownership
|
|
|
|
|
}
|
2026-07-15 19:25:50 +09:00
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
#[must_use]
|
|
|
|
|
pub const fn network_sidecar_id(&self) -> Option<NetworkSidecarId> {
|
|
|
|
|
self.network_sidecar_id
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
#[must_use]
|
|
|
|
|
pub const fn has_git_routing(&self) -> bool {
|
|
|
|
|
self.git_routing.is_some()
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 15:20:13 +09:00
|
|
|
#[must_use]
|
|
|
|
|
pub const fn runsc_command(&self) -> &RunscCommand {
|
|
|
|
|
&self.runsc_command
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:25:50 +09:00
|
|
|
/// # 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<ChildStandardIo> {
|
|
|
|
|
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,
|
|
|
|
|
))
|
|
|
|
|
}
|
2026-07-15 19:09:24 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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<io::Result<()>> {
|
|
|
|
|
if arguments
|
|
|
|
|
.first()
|
2026-07-19 03:44:30 +09:00
|
|
|
.is_none_or(|argument| argument != PRIVATE_RUNTIME_ROLE)
|
2026-07-15 19:09:24 +09:00
|
|
|
{
|
|
|
|
|
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,
|
2026-07-16 17:29:23 +09:00
|
|
|
state_anchor: Option<RuntimeStateAnchorArguments>,
|
2026-07-18 02:08:14 +09:00
|
|
|
git_routing: Option<RuntimeGitRoutingArguments>,
|
2026-07-15 23:26:37 +09:00
|
|
|
network: Option<RuntimeNetworkArguments>,
|
2026-07-15 19:09:24 +09:00
|
|
|
runsc_program: OsString,
|
|
|
|
|
runsc_arguments: Vec<OsString>,
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
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<PathBuf>,
|
|
|
|
|
isolate_ignored: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
struct RuntimeStateAnchorArguments {
|
|
|
|
|
lower_fd: i32,
|
2026-07-17 03:58:27 +09:00
|
|
|
lower_label: PathBuf,
|
|
|
|
|
persistent_root_fd: i32,
|
2026-07-16 17:29:23 +09:00
|
|
|
persistent_anchor_fd: i32,
|
2026-07-17 03:58:27 +09:00
|
|
|
persistent_anchor_label: PathBuf,
|
|
|
|
|
runtime_root_fd: i32,
|
2026-07-16 17:29:23 +09:00
|
|
|
runtime_anchor_fd: i32,
|
2026-07-17 03:58:27 +09:00
|
|
|
runtime_anchor_label: PathBuf,
|
2026-07-16 17:29:23 +09:00
|
|
|
expected_lower: (u64, u64),
|
2026-07-17 03:58:27 +09:00
|
|
|
persistent_destinations: Vec<PathBuf>,
|
2026-07-16 17:29:23 +09:00
|
|
|
}
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
struct RuntimeNetworkArguments {
|
|
|
|
|
policy_fd: i32,
|
|
|
|
|
policy_digest: DerivedPolicyDigest,
|
|
|
|
|
configuration_fingerprint: ConfigurationFingerprint,
|
|
|
|
|
network_sidecar_id: NetworkSidecarId,
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
#[allow(clippy::too_many_lines)]
|
2026-07-15 19:09:24 +09:00
|
|
|
fn parse_runtime_arguments(arguments: &[OsString]) -> io::Result<RuntimeArguments> {
|
2026-07-15 23:26:37 +09:00
|
|
|
let mut arguments = arguments.iter().peekable();
|
2026-07-15 19:09:24 +09:00
|
|
|
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")?)?;
|
2026-07-16 17:29:23 +09:00
|
|
|
let state_anchor = if arguments
|
|
|
|
|
.peek()
|
|
|
|
|
.is_some_and(|value| *value == "--state-lower-fd")
|
|
|
|
|
{
|
2026-07-17 03:58:27 +09:00
|
|
|
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::<io::Result<Vec<_>>>()?;
|
2026-07-16 17:29:23 +09:00
|
|
|
Some(RuntimeStateAnchorArguments {
|
2026-07-17 03:58:27 +09:00
|
|
|
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,
|
2026-07-16 17:29:23 +09:00
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2026-07-18 02:08:14 +09:00
|
|
|
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
|
|
|
|
|
};
|
2026-07-15 23:26:37 +09:00
|
|
|
let network = if arguments
|
|
|
|
|
.peek()
|
|
|
|
|
.is_some_and(|value| *value == "--network-policy-fd")
|
|
|
|
|
{
|
|
|
|
|
Some(parse_network_arguments(&mut arguments)?)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
2026-07-15 19:09:24 +09:00
|
|
|
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,
|
|
|
|
|
},
|
2026-07-16 17:29:23 +09:00
|
|
|
state_anchor,
|
2026-07-18 02:08:14 +09:00
|
|
|
git_routing,
|
2026-07-15 23:26:37 +09:00
|
|
|
network,
|
2026-07-15 19:09:24 +09:00
|
|
|
runsc_program: (*program).clone(),
|
|
|
|
|
runsc_arguments: arguments.cloned().collect(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-16 17:29:23 +09:00
|
|
|
fn parse_number<T>(value: &OsStr) -> io::Result<T>
|
|
|
|
|
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"))
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-18 02:08:14 +09:00
|
|
|
fn parse_descriptor(value: &OsStr) -> io::Result<i32> {
|
|
|
|
|
parse_number(value)
|
|
|
|
|
.ok()
|
|
|
|
|
.filter(|descriptor| *descriptor >= 0)
|
|
|
|
|
.ok_or_else(|| io::Error::other("Runtime Supervisor descriptor is invalid"))
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 23:26:37 +09:00
|
|
|
fn parse_network_arguments<'a>(
|
|
|
|
|
arguments: &mut impl Iterator<Item = &'a OsString>,
|
|
|
|
|
) -> io::Result<RuntimeNetworkArguments> {
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 19:09:24 +09:00
|
|
|
fn named<'a>(
|
|
|
|
|
arguments: &mut impl Iterator<Item = &'a OsString>,
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-19 03:02:44 +09:00
|
|
|
include!("supervision/startup.rs");
|
|
|
|
|
include!("supervision/runtime.rs");
|
|
|
|
|
include!("supervision/tests.rs");
|