#[cfg(unix)]
use std::os::fd::BorrowedFd;
#[cfg(windows)]
use std::os::windows::io::BorrowedHandle;
use std::process::Command;
use std::time::Duration;
pub struct SpawnStdio<'a> {
pub stdin: StdioSource<'a>,
pub stdout: StdioSource<'a>,
pub stderr: StdioSource<'a>,
pub drain_timeout: Option<Duration>,
pub show_console: bool,
}
impl Default for SpawnStdio<'_> {
fn default() -> Self {
Self {
stdin: StdioSource::Null,
stdout: StdioSource::Parent,
stderr: StdioSource::Parent,
drain_timeout: Some(Duration::from_secs(2)),
show_console: false,
}
}
}
pub enum StdioSource<'a> {
Null,
Parent,
#[cfg(windows)]
Handle(BorrowedHandle<'a>),
#[cfg(unix)]
Fd(BorrowedFd<'a>),
Pipe,
#[doc(hidden)]
_Phantom(std::marker::PhantomData<&'a ()>),
}
pub struct DaemonChild {
pid: u32,
#[cfg(windows)]
handle: imp::OwnedHandle,
#[cfg(unix)]
child: std::process::Child,
}
impl DaemonChild {
pub fn id(&self) -> u32 {
self.pid
}
pub fn kill(&mut self) -> std::io::Result<()> {
#[cfg(windows)]
{
imp::terminate(&self.handle)
}
#[cfg(unix)]
{
self.child.kill()
}
}
pub fn wait(&mut self) -> std::io::Result<i32> {
#[cfg(windows)]
{
imp::wait(&self.handle)
}
#[cfg(unix)]
{
let status = self.child.wait()?;
Ok(unix_exit_code(status))
}
}
pub fn try_wait(&mut self) -> std::io::Result<Option<i32>> {
#[cfg(windows)]
{
imp::try_wait(&self.handle)
}
#[cfg(unix)]
{
Ok(self.child.try_wait()?.map(unix_exit_code))
}
}
}
pub struct SpawnedChild {
pub stdin: Option<std::process::ChildStdin>,
pub stdout: Option<std::process::ChildStdout>,
pub stderr: Option<std::process::ChildStderr>,
pid: u32,
#[cfg(windows)]
inner: imp::SpawnedInner,
#[cfg(unix)]
inner: unix_impl::SpawnedInner,
}
impl SpawnedChild {
pub fn id(&self) -> u32 {
self.pid
}
pub fn kill(&mut self) -> std::io::Result<()> {
#[cfg(windows)]
{
self.inner.kill()
}
#[cfg(unix)]
{
self.inner.kill()
}
}
pub fn wait(&mut self) -> std::io::Result<i32> {
#[cfg(windows)]
{
self.inner.wait()
}
#[cfg(unix)]
{
self.inner.wait()
}
}
pub fn try_wait(&mut self) -> std::io::Result<Option<i32>> {
#[cfg(windows)]
{
self.inner.try_wait()
}
#[cfg(unix)]
{
self.inner.try_wait()
}
}
}
impl Drop for SpawnedChild {
fn drop(&mut self) {
#[cfg(windows)]
{
self.inner.shutdown();
}
#[cfg(unix)]
{
self.inner.shutdown();
}
}
}
pub fn spawn_daemon(command: &mut Command) -> std::io::Result<DaemonChild> {
spawn_daemon_with_clear_env(command, false)
}
pub fn spawn_daemon_with_clear_env(
command: &mut Command,
clear_env: bool,
) -> std::io::Result<DaemonChild> {
#[cfg(windows)]
{
imp::spawn_daemon(command, clear_env)
}
#[cfg(unix)]
{
unix_impl::spawn_daemon(command, clear_env)
}
}
pub fn spawn(command: &mut Command, stdio: SpawnStdio<'_>) -> std::io::Result<SpawnedChild> {
#[cfg(windows)]
{
imp::spawn(command, stdio)
}
#[cfg(unix)]
{
unix_impl::spawn(command, stdio)
}
}
#[cfg(unix)]
fn unix_exit_code(status: std::process::ExitStatus) -> i32 {
use std::os::unix::process::ExitStatusExt;
status
.code()
.unwrap_or_else(|| -status.signal().unwrap_or(1))
}
#[cfg(windows)]
#[path = "spawn_imp_windows.rs"]
mod imp;
#[cfg(unix)]
#[path = "spawn_imp_unix.rs"]
mod unix_impl;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spawn_stdio_default_has_sane_values() {
let s = SpawnStdio::default();
assert!(matches!(s.stdin, StdioSource::Null));
assert!(matches!(s.stdout, StdioSource::Parent));
assert!(matches!(s.stderr, StdioSource::Parent));
assert_eq!(s.drain_timeout, Some(Duration::from_secs(2)));
assert!(!s.show_console);
}
}