1use std::fmt;
4use std::fs::File;
5use std::io;
6use std::os::windows::io::{AsHandle, BorrowedHandle, OwnedHandle};
7
8use crate::child::Child;
9use crate::sys;
10
11pub struct Stdio {
13 pub(crate) inner: StdioInner,
14}
15
16pub(crate) enum StdioInner {
17 Inherit,
18 Null,
19 Piped,
20 Owned(OwnedHandle),
21}
22
23impl fmt::Debug for Stdio {
24 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
25 let name = match self.inner {
26 StdioInner::Inherit => "Inherit",
27 StdioInner::Null => "Null",
28 StdioInner::Piped => "Piped",
29 StdioInner::Owned(_) => "Owned",
30 };
31 formatter.debug_tuple("Stdio").field(&name).finish()
32 }
33}
34
35impl Stdio {
36 #[must_use]
38 pub const fn inherit() -> Self {
39 Self {
40 inner: StdioInner::Inherit,
41 }
42 }
43
44 #[must_use]
46 pub const fn null() -> Self {
47 Self {
48 inner: StdioInner::Null,
49 }
50 }
51
52 #[must_use]
54 pub const fn piped() -> Self {
55 Self {
56 inner: StdioInner::Piped,
57 }
58 }
59
60 pub fn from_borrowed<T: AsHandle>(source: &T) -> io::Result<Self> {
68 Ok(Self::from(sys::duplicate_local(source.as_handle(), false)?))
69 }
70}
71
72impl From<OwnedHandle> for Stdio {
73 fn from(handle: OwnedHandle) -> Self {
74 Self {
75 inner: StdioInner::Owned(handle),
76 }
77 }
78}
79
80impl From<File> for Stdio {
81 fn from(file: File) -> Self {
82 Self::from(OwnedHandle::from(file))
83 }
84}
85
86#[derive(Debug)]
88pub struct ParentProcess {
89 handle: OwnedHandle,
90}
91
92impl ParentProcess {
93 pub fn open(pid: u32) -> io::Result<Self> {
99 Ok(Self {
100 handle: sys::open_parent_process(pid)?,
101 })
102 }
103
104 pub fn from_handle(handle: OwnedHandle) -> io::Result<Self> {
110 sys::validate_process_handle(handle.as_handle())?;
111 Ok(Self { handle })
112 }
113}
114
115impl AsHandle for ParentProcess {
116 fn as_handle(&self) -> BorrowedHandle<'_> {
117 self.handle.as_handle()
118 }
119}
120
121#[derive(Debug)]
123pub struct Job {
124 handle: OwnedHandle,
125}
126
127impl Job {
128 pub fn create() -> io::Result<Self> {
134 Ok(Self {
135 handle: sys::create_job()?,
136 })
137 }
138
139 pub fn from_handle(handle: OwnedHandle) -> io::Result<Self> {
145 sys::validate_job_handle(handle.as_handle())?;
146 Ok(Self { handle })
147 }
148
149 pub fn duplicate(&self) -> io::Result<Self> {
155 Ok(Self {
156 handle: sys::duplicate_local(self.handle.as_handle(), false)?,
157 })
158 }
159
160 pub fn assign(&self, child: &Child) -> io::Result<()> {
166 sys::assign_job(self.handle.as_handle(), child.process_handle())
167 }
168
169 pub fn terminate(&self, exit_code: u32) -> io::Result<()> {
175 sys::terminate_job(self.handle.as_handle(), exit_code)
176 }
177
178 pub fn set_kill_on_close(&self, enable: bool) -> io::Result<()> {
184 sys::set_job_kill_on_close(self.handle.as_handle(), enable)
185 }
186}
187
188impl AsHandle for Job {
189 fn as_handle(&self) -> BorrowedHandle<'_> {
190 self.handle.as_handle()
191 }
192}
193
194#[allow(unsafe_code)]
204pub unsafe trait AsPseudoConsole {
205 fn raw_pseudoconsole(&self) -> isize;
215}
216
217#[cfg(test)]
218mod tests {
219 use std::os::windows::io::AsRawHandle;
220
221 use super::*;
222
223 #[test]
224 fn owned_handle_adoption_validates_resource_kind() {
225 let mut host = std::process::Command::new("cmd.exe")
226 .args(["/D", "/C", "ping -n 5 127.0.0.1 >nul"])
227 .spawn()
228 .unwrap();
229 let parent = ParentProcess::open(host.id()).unwrap();
230 assert!(format!("{parent:?}").contains("ParentProcess"));
231 let adopted_parent =
232 ParentProcess::from_handle(sys::duplicate_local(host.as_handle(), false).unwrap())
233 .unwrap();
234 assert_ne!(
235 adopted_parent.as_handle().as_raw_handle(),
236 std::ptr::null_mut()
237 );
238
239 let job = Job::create().unwrap();
240 let duplicate = job.duplicate().unwrap();
241 let adopted_job = Job::from_handle(duplicate.handle).unwrap();
242 adopted_job.set_kill_on_close(true).unwrap();
243 adopted_job.set_kill_on_close(false).unwrap();
244
245 let file = File::open("NUL").unwrap();
246 let not_process = sys::duplicate_local(file.as_handle(), false).unwrap();
247 assert!(ParentProcess::from_handle(not_process).is_err());
248 let not_job = sys::duplicate_local(file.as_handle(), false).unwrap();
249 assert!(Job::from_handle(not_job).is_err());
250 let _ = host.kill();
251 let _ = host.wait();
252 }
253}