Implement supervised rootless network readiness

This commit is contained in:
ms2k 2026-07-15 23:26:37 +09:00
commit ba6a075839

View file

@ -272,6 +272,55 @@ fn require_captured_parent_edge(parent_edge: ParentEdge) -> io::Result<()> {
}
impl ChildBroker {
/// Runs a non-durable direct child under the same spawn/reap exclusion as owned children.
///
/// # Errors
/// Returns an error when causal launch, exact waiting, or the command exit status fails.
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
pub fn run_transient(&self, target: &TargetCommand) -> io::Result<ProcessExit> {
let _guard = self
.exclusion
.lock()
.map_err(|_| io::Error::other("child broker lock poisoned"))?;
require_parent_edge_standard_io(target.parent_edge(), ChildStandardIoPolicy::Silenced)?;
require_default_sigchld()?;
require_reapable_sigchld()?;
let parent = current_process_identity()?;
let (release, wait) = launch_barrier()?;
let mut command = private_role_command(WRAPPER_ROLE);
append_parent_identity(&mut command, parent);
target.append_to(&mut command);
command
.stdin(wait.into_stdio())
.stdout(Stdio::null())
.stderr(Stdio::null());
let mut child = command.spawn()?;
let process = match acquire_spawned_child(child.id()) {
Ok(process) => process,
Err(error) => return fail_before_pidfd(release, &mut child, error),
};
if let Err(error) = release.release() {
return fail_after_pidfd_closed(process, error);
}
drop(child);
let status =
rustix::process::waitid(WaitId::PidFd(process.pidfd.as_fd()), WaitIdOptions::EXITED)?
.ok_or_else(|| io::Error::other("transient child exited without wait status"))?;
let exit = ProcessExit {
exit_code: status.exit_status(),
terminating_signal: status.terminating_signal(),
};
drop(process);
if exit.exit_code == Some(0) && exit.terminating_signal.is_none() {
Ok(exit)
} else {
Err(io::Error::other(format!(
"transient child failed with exit {:?} and signal {:?}",
exit.exit_code, exit.terminating_signal
)))
}
}
/// Spawns the repository wrapper, stages exact ownership, accepts its acknowledgment,
/// and only then releases target execution.
///
@ -308,13 +357,46 @@ impl ChildBroker {
expected_parent: PidfsProcessIdentityV1,
ownership: ProcessOwnershipContext,
target: &TargetCommand,
mut standard_io: ChildStandardIo,
standard_io: ChildStandardIo,
stage: Stage,
acknowledge: Acknowledge,
) -> io::Result<RetainedProcess>
where
Stage: FnOnce(StagedProcessOwnership) -> io::Result<()>,
Acknowledge: FnOnce(StagedProcessOwnership) -> io::Result<ProcessOwnershipCommitAck>,
{
self.acquire_with_stdio_after_ack(
expected_parent,
ownership,
target,
standard_io,
stage,
acknowledge,
|| Ok(()),
)
}
/// Acquires an exact child while retaining its blocked wrapper across one acknowledged
/// parent transition.
///
/// # Errors
/// Refuses and reaps the blocked wrapper when staging, acknowledgment, transition, or release
/// fails.
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
pub fn acquire_with_stdio_after_ack<Stage, Acknowledge, Transition>(
&self,
expected_parent: PidfsProcessIdentityV1,
ownership: ProcessOwnershipContext,
target: &TargetCommand,
mut standard_io: ChildStandardIo,
stage: Stage,
acknowledge: Acknowledge,
transition: Transition,
) -> io::Result<RetainedProcess>
where
Stage: FnOnce(StagedProcessOwnership) -> io::Result<()>,
Acknowledge: FnOnce(StagedProcessOwnership) -> io::Result<ProcessOwnershipCommitAck>,
Transition: FnOnce() -> io::Result<()>,
{
let _guard = self
.exclusion
@ -364,6 +446,9 @@ impl ChildBroker {
),
);
}
if let Err(error) = transition() {
return fail_after_pidfd(release, process, error);
}
if let Err(error) = release.release() {
return fail_after_pidfd_closed(process, error);
}