Decompose oversized Rust files by responsibility
This commit is contained in:
parent
03bfe92ca0
commit
ee9201b057
1 changed files with 18648 additions and 18513 deletions
196
crates/lazaret-linux/src/process/launch.rs
Normal file
196
crates/lazaret-linux/src/process/launch.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/// # Errors
|
||||
/// Returns an error when the fail-closed pipe cannot be created.
|
||||
pub fn launch_barrier() -> io::Result<(BarrierRelease, BarrierWait)> {
|
||||
let (reader, writer) = pipe_with(PipeFlags::empty())?;
|
||||
fcntl_setfd(writer.as_fd(), FdFlags::CLOEXEC)?;
|
||||
Ok((
|
||||
BarrierRelease(File::from(writer)),
|
||||
BarrierWait(File::from(reader)),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BarrierRelease(File);
|
||||
|
||||
impl BarrierRelease {
|
||||
/// # Errors
|
||||
/// Returns an error unless the complete release token is written.
|
||||
pub fn release(mut self) -> io::Result<()> {
|
||||
self.0.write_all(BARRIER_RELEASE)?;
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BarrierWait(File);
|
||||
|
||||
impl BarrierWait {
|
||||
#[must_use]
|
||||
pub fn into_stdio(self) -> Stdio {
|
||||
Stdio::from(self.0)
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Returns an error when the inherited standard-input descriptor cannot be duplicated.
|
||||
pub fn from_stdin() -> io::Result<Self> {
|
||||
Ok(Self(File::open("/proc/self/fd/0")?))
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Refuses EOF, partial, altered, or overlong releases.
|
||||
pub fn wait(mut self) -> io::Result<()> {
|
||||
let mut release = [0_u8; BARRIER_RELEASE.len()];
|
||||
self.0.read_exact(&mut release)?;
|
||||
let mut excess = [0_u8; 1];
|
||||
if release != *BARRIER_RELEASE || self.0.read(&mut excess)? != 0 {
|
||||
return Err(io::Error::other("malformed launch-barrier release"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub fn append_parent_identity(
|
||||
command: &mut std::process::Command,
|
||||
identity: PidfsProcessIdentityV1,
|
||||
) {
|
||||
let authority = identity.authority();
|
||||
command
|
||||
.arg("--parent-boot")
|
||||
.arg(hex(authority.boot_id.as_bytes()))
|
||||
.arg("--parent-device")
|
||||
.arg(authority.pidfs_st_dev.get().to_string())
|
||||
.arg("--parent-inode")
|
||||
.arg(authority.pidfs_st_ino.get().to_string())
|
||||
.arg("--parent-pid")
|
||||
.arg(identity.host_pid().get().to_string())
|
||||
.arg("--parent-start")
|
||||
.arg(identity.start_time_ticks().get().to_string());
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// Refuses missing, reordered, malformed, or incomplete wrapper arguments.
|
||||
pub fn parse_wrapper_arguments(
|
||||
arguments: impl IntoIterator<Item = OsString>,
|
||||
) -> io::Result<(PidfsProcessIdentityV1, TargetCommand)> {
|
||||
let mut arguments = arguments.into_iter();
|
||||
let boot = named_argument(&mut arguments, "--parent-boot")?;
|
||||
let device = number_argument(&mut arguments, "--parent-device")?;
|
||||
let inode = number_argument(&mut arguments, "--parent-inode")?;
|
||||
let pid = number_argument(&mut arguments, "--parent-pid")?;
|
||||
let start = number_argument(&mut arguments, "--parent-start")?;
|
||||
let parent_edge = match named_argument(&mut arguments, "--parent-action")?.to_str() {
|
||||
Some("kill") => ParentEdge::ParentDeathKill,
|
||||
Some("lifetime-pipe") => ParentEdge::LifetimePipe,
|
||||
_ => return Err(io::Error::other("wrapper parent action is invalid")),
|
||||
};
|
||||
let next = arguments.next();
|
||||
let stdin = if next.as_deref() == Some(OsStr::new("--target-stdin-fd")) {
|
||||
let descriptor = arguments
|
||||
.next()
|
||||
.and_then(|value| value.to_str().and_then(|value| value.parse::<i32>().ok()))
|
||||
.filter(|descriptor| *descriptor > 2)
|
||||
.ok_or_else(|| io::Error::other("wrapper target stdin descriptor is invalid"))?;
|
||||
let access = TargetStdinAccess::from_descriptor(descriptor)?;
|
||||
if arguments.next().as_deref() != Some(OsStr::new("--")) {
|
||||
return Err(io::Error::other("wrapper target separator is missing"));
|
||||
}
|
||||
Some(access.reopen(descriptor)?)
|
||||
} else if next.as_deref() == Some(OsStr::new("--")) {
|
||||
None
|
||||
} else {
|
||||
return Err(io::Error::other("wrapper target separator is missing"));
|
||||
};
|
||||
let program = arguments
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::other("wrapper target is missing"))?;
|
||||
let target = TargetCommand {
|
||||
program,
|
||||
arguments: arguments.collect(),
|
||||
parent_edge,
|
||||
executable: None,
|
||||
stdin,
|
||||
};
|
||||
let boot = parse_boot_hex(&boot)?;
|
||||
Ok((
|
||||
PidfsProcessIdentityV1::new(
|
||||
PidfsProcessAuthorityV1 {
|
||||
boot_id: boot,
|
||||
pidfs_st_dev: PidfsDeviceId::new(device),
|
||||
pidfs_st_ino: PidfsInodeId::new(inode),
|
||||
},
|
||||
HostPidLocator::new(
|
||||
u32::try_from(pid).map_err(|_| io::Error::other("parent PID is out of range"))?,
|
||||
),
|
||||
ProcessStartTimeTicks::new(start),
|
||||
),
|
||||
target,
|
||||
))
|
||||
}
|
||||
|
||||
fn named_argument(
|
||||
arguments: &mut impl Iterator<Item = OsString>,
|
||||
name: &str,
|
||||
) -> io::Result<OsString> {
|
||||
if arguments.next().as_deref() != Some(OsStr::new(name)) {
|
||||
return Err(io::Error::other(format!(
|
||||
"wrapper argument {name} is missing"
|
||||
)));
|
||||
}
|
||||
arguments
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::other(format!("wrapper argument {name} has no value")))
|
||||
}
|
||||
|
||||
fn number_argument(arguments: &mut impl Iterator<Item = OsString>, name: &str) -> io::Result<u64> {
|
||||
named_argument(arguments, name)?
|
||||
.to_str()
|
||||
.and_then(|value| value.parse().ok())
|
||||
.ok_or_else(|| io::Error::other(format!("wrapper argument {name} is invalid")))
|
||||
}
|
||||
|
||||
fn parse_boot_hex(value: &OsStr) -> io::Result<BootId> {
|
||||
let value = value
|
||||
.to_str()
|
||||
.ok_or_else(|| io::Error::other("parent boot ID is not UTF-8"))?;
|
||||
if value.len() != 32 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
|
||||
return Err(io::Error::other("parent boot ID is invalid"));
|
||||
}
|
||||
let mut bytes = [0_u8; 16];
|
||||
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("parent boot ID is invalid"))?;
|
||||
}
|
||||
Ok(BootId::new(bytes))
|
||||
}
|
||||
|
||||
fn hex(bytes: &[u8]) -> String {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
bytes.iter().fold(String::new(), |mut output, byte| {
|
||||
write!(output, "{byte:02x}").expect("writing to a String cannot fail");
|
||||
output
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_methods, clippy::disallowed_types)]
|
||||
#[must_use]
|
||||
pub fn exec_target(target: TargetCommand) -> io::Error {
|
||||
use std::os::unix::process::CommandExt;
|
||||
|
||||
std::process::Command::new(target.program)
|
||||
.args(target.arguments)
|
||||
.exec()
|
||||
}
|
||||
|
||||
/// Stops this process until the host resumes it.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error when the kernel refuses the stop signal.
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
pub fn suspend_current_process() -> io::Result<()> {
|
||||
Ok(rustix::process::kill_process(
|
||||
rustix::process::getpid(),
|
||||
Signal::STOP,
|
||||
)?)
|
||||
}
|
||||
Loading…
Reference in a new issue