use std::fmt;
use std::fs::File;
use std::io;
use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
use crate::child::Child;
use crate::sys;
pub struct Stdio {
pub(crate) inner: StdioInner,
}
pub(crate) enum StdioInner {
Inherit,
Null,
Piped,
Owned(OwnedHandle),
}
impl fmt::Debug for Stdio {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self.inner {
StdioInner::Inherit => "Inherit",
StdioInner::Null => "Null",
StdioInner::Piped => "Piped",
StdioInner::Owned(_) => "Owned",
};
formatter.debug_tuple("Stdio").field(&name).finish()
}
}
impl Stdio {
#[must_use]
pub const fn inherit() -> Self {
Self {
inner: StdioInner::Inherit,
}
}
#[must_use]
pub const fn null() -> Self {
Self {
inner: StdioInner::Null,
}
}
#[must_use]
pub const fn piped() -> Self {
Self {
inner: StdioInner::Piped,
}
}
pub fn from_borrowed<T: AsHandle>(source: &T) -> io::Result<Self> {
Ok(Self::from(sys::duplicate_local(source.as_handle(), false)?))
}
}
impl From<OwnedHandle> for Stdio {
fn from(handle: OwnedHandle) -> Self {
Self {
inner: StdioInner::Owned(handle),
}
}
}
impl From<File> for Stdio {
fn from(file: File) -> Self {
Self::from(OwnedHandle::from(file))
}
}
#[derive(Debug)]
pub struct ParentProcess {
handle: OwnedHandle,
}
impl ParentProcess {
pub fn open(pid: u32) -> io::Result<Self> {
Ok(Self {
handle: sys::open_parent_process(pid)?,
})
}
pub fn from_handle(handle: OwnedHandle) -> io::Result<Self> {
sys::validate_process_handle(handle.as_handle())?;
Ok(Self { handle })
}
}
impl AsHandle for ParentProcess {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.handle.as_handle()
}
}
#[derive(Debug)]
pub struct Job {
handle: OwnedHandle,
}
impl Job {
pub fn create() -> io::Result<Self> {
Ok(Self {
handle: sys::create_job()?,
})
}
pub fn from_handle(handle: OwnedHandle) -> io::Result<Self> {
sys::validate_job_handle(handle.as_handle())?;
Ok(Self { handle })
}
pub fn duplicate(&self) -> io::Result<Self> {
Ok(Self {
handle: sys::duplicate_local(self.handle.as_handle(), false)?,
})
}
pub fn assign(&self, child: &Child) -> io::Result<()> {
sys::assign_job(self.handle.as_handle(), child.process_handle())
}
pub fn terminate(&self, exit_code: u32) -> io::Result<()> {
sys::terminate_job(self.handle.as_handle(), exit_code)
}
pub fn set_kill_on_close(&self, enable: bool) -> io::Result<()> {
sys::set_job_kill_on_close(self.handle.as_handle(), enable)
}
}
impl AsHandle for Job {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.handle.as_handle()
}
}
#[allow(unsafe_code)]
pub unsafe trait AsPseudoConsole {
fn raw_pseudoconsole(&self) -> isize;
}
#[cfg(test)]
mod tests {
use std::os::windows::io::AsRawHandle;
use super::*;
#[test]
fn owned_handle_adoption_validates_resource_kind() {
let mut host = std::process::Command::new("cmd.exe")
.args(["/D", "/C", "ping -n 5 127.0.0.1 >nul"])
.spawn()
.unwrap();
let parent = ParentProcess::open(host.id()).unwrap();
assert!(format!("{parent:?}").contains("ParentProcess"));
let adopted_parent =
ParentProcess::from_handle(sys::duplicate_local(host.as_handle(), false).unwrap())
.unwrap();
assert_ne!(
adopted_parent.as_handle().as_raw_handle(),
std::ptr::null_mut()
);
let job = Job::create().unwrap();
let duplicate = job.duplicate().unwrap();
let adopted_job = Job::from_handle(duplicate.handle).unwrap();
adopted_job.set_kill_on_close(true).unwrap();
adopted_job.set_kill_on_close(false).unwrap();
let file = File::open("NUL").unwrap();
let not_process = sys::duplicate_local(file.as_handle(), false).unwrap();
assert!(ParentProcess::from_handle(not_process).is_err());
let not_job = sys::duplicate_local(file.as_handle(), false).unwrap();
assert!(Job::from_handle(not_job).is_err());
let _ = host.kill();
let _ = host.wait();
}
}