1#![allow(
27 clippy::undocumented_unsafe_blocks,
28 reason = "ported code from std/tokio with existing safety context"
29)]
30#![cfg(windows)]
31
32#[cfg(test)]
33mod tests;
34
35mod process;
36mod process_stdio;
37
38mod anon_pipe;
39mod env;
40mod uv_error;
41mod widestr;
42
43use std::borrow::Cow;
44use std::ffi::OsStr;
45use std::ffi::OsString;
46use std::future::Future;
47use std::io;
48use std::io::Read;
49use std::os::windows::io::FromRawHandle;
50use std::os::windows::io::IntoRawHandle;
51use std::os::windows::process::ExitStatusExt;
52use std::os::windows::raw::HANDLE;
53use std::path::Path;
54use std::pin::Pin;
55use std::process::ChildStderr;
56use std::process::ChildStdin;
57use std::process::ChildStdout;
58use std::process::ExitStatus;
59use std::process::Output;
60use std::task::Context;
61use std::task::Poll;
62
63use anon_pipe::read2;
64use env::CommandEnv;
65pub use process::process_kill;
66pub use process_stdio::disable_stdio_inheritance;
67
68use crate::process::*;
69use crate::process_stdio::*;
70
71#[derive(Debug)]
72pub enum Stdio {
73 Inherit,
74 Pipe,
75 Null,
76 RawHandle(HANDLE),
77}
78
79impl From<Stdio> for std::process::Stdio {
80 fn from(stdio: Stdio) -> Self {
81 match stdio {
82 Stdio::Inherit => std::process::Stdio::inherit(),
83 Stdio::Pipe => std::process::Stdio::piped(),
84 Stdio::Null => std::process::Stdio::null(),
85 Stdio::RawHandle(handle) => unsafe {
86 std::process::Stdio::from_raw_handle(handle)
87 },
88 }
89 }
90}
91
92impl Stdio {
93 pub fn inherit() -> Self {
94 Stdio::Inherit
95 }
96
97 pub fn piped() -> Self {
98 Stdio::Pipe
99 }
100
101 pub fn null() -> Self {
102 Stdio::Null
103 }
104}
105
106impl<T: IntoRawHandle> From<T> for Stdio {
107 fn from(value: T) -> Self {
108 Stdio::RawHandle(value.into_raw_handle())
109 }
110}
111
112pub struct Child {
113 inner: FusedChild,
114 pub stdin: Option<ChildStdin>,
115 pub stdout: Option<ChildStdout>,
116 pub stderr: Option<ChildStderr>,
117}
118
119pub(crate) trait Kill {
122 fn kill(&mut self) -> io::Result<()>;
124}
125
126impl<T: Kill> Kill for &mut T {
127 fn kill(&mut self) -> io::Result<()> {
128 (**self).kill()
129 }
130}
131
132#[derive(Debug)]
136struct ChildDropGuard<T: Kill> {
137 inner: T,
138 kill_on_drop: bool,
139}
140
141impl<T: Kill> Kill for ChildDropGuard<T> {
142 fn kill(&mut self) -> io::Result<()> {
143 let ret = self.inner.kill();
144
145 if ret.is_ok() {
146 self.kill_on_drop = false;
147 }
148
149 ret
150 }
151}
152
153impl<T: Kill> Drop for ChildDropGuard<T> {
154 fn drop(&mut self) {
155 if self.kill_on_drop {
156 drop(self.kill());
157 }
158 }
159}
160
161impl<T, E, F> Future for ChildDropGuard<F>
163where
164 F: Future<Output = Result<T, E>> + Kill + Unpin,
165{
166 type Output = Result<T, E>;
167
168 fn poll(
169 mut self: Pin<&mut Self>,
170 cx: &mut Context<'_>,
171 ) -> Poll<Self::Output> {
172 let ret = Pin::new(&mut self.inner).poll(cx);
173
174 if let Poll::Ready(Ok(_)) = ret {
175 self.kill_on_drop = false;
177 }
178
179 ret
180 }
181}
182
183#[derive(Debug)]
188enum FusedChild {
189 Child(ChildDropGuard<ChildProcess>),
190 Done(i32),
191}
192
193impl Child {
194 pub fn id(&self) -> Option<u32> {
195 match &self.inner {
196 FusedChild::Child(child) => Some(child.inner.pid() as u32),
197 FusedChild::Done(_) => None,
198 }
199 }
200
201 pub fn wait_blocking(&mut self) -> Result<ExitStatus, std::io::Error> {
202 drop(self.stdin.take());
203 match &mut self.inner {
204 FusedChild::Child(child) => child
205 .inner
206 .wait()
207 .map(|code| ExitStatus::from_raw(code as u32)),
208 FusedChild::Done(code) => Ok(ExitStatus::from_raw(*code as u32)),
209 }
210 }
211
212 pub async fn wait(&mut self) -> io::Result<ExitStatus> {
213 drop(self.stdin.take());
216
217 match &mut self.inner {
218 FusedChild::Done(exit) => Ok(ExitStatus::from_raw(*exit as u32)),
219 FusedChild::Child(child) => {
220 let ret = child.await;
221
222 if let Ok(exit) = ret {
223 self.inner = FusedChild::Done(exit);
224 }
225
226 ret.map(|code| ExitStatus::from_raw(code as u32))
227 }
228 }
229 }
230
231 pub fn try_wait(&mut self) -> Result<Option<i32>, std::io::Error> {
232 match &mut self.inner {
233 FusedChild::Done(exit) => Ok(Some(*exit)),
234 FusedChild::Child(child) => child.inner.try_wait(),
235 }
236 }
237
238 pub fn wait_with_output(&mut self) -> io::Result<Output> {
240 drop(self.stdin.take());
241
242 let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
243 match (self.stdout.take(), self.stderr.take()) {
244 (None, None) => {}
245 (Some(mut out), None) => {
246 let res = out.read_to_end(&mut stdout);
247 res.unwrap();
248 }
249 (None, Some(mut err)) => {
250 let res = err.read_to_end(&mut stderr);
251 res.unwrap();
252 }
253 (Some(out), Some(err)) => {
254 let res = read2(
255 unsafe {
256 crate::anon_pipe::AnonPipe::from_raw_handle(out.into_raw_handle())
257 },
258 &mut stdout,
259 unsafe {
260 crate::anon_pipe::AnonPipe::from_raw_handle(err.into_raw_handle())
261 },
262 &mut stderr,
263 );
264 res.unwrap();
265 }
266 }
267
268 let status = self.wait_blocking()?;
269 Ok(Output {
270 status,
271 stdout,
272 stderr,
273 })
274 }
275}
276
277pub struct Command {
278 program: OsString,
279 args: Vec<OsString>,
280 envs: CommandEnv,
281 detached: bool,
282 cwd: Option<OsString>,
283 stdin: Stdio,
284 stdout: Stdio,
285 stderr: Stdio,
286 extra_handles: Vec<Option<HANDLE>>,
287 kill_on_drop: bool,
288 verbatim_arguments: bool,
289}
290
291impl Command {
292 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
293 Self {
294 program: program.as_ref().to_os_string(),
295 args: vec![program.as_ref().to_os_string()],
296 envs: CommandEnv::default(),
297 detached: false,
298 cwd: None,
299 stdin: Stdio::Inherit,
300 stdout: Stdio::Inherit,
301 stderr: Stdio::Inherit,
302 extra_handles: vec![],
303 kill_on_drop: false,
304 verbatim_arguments: false,
305 }
306 }
307
308 pub fn verbatim_arguments(&mut self, verbatim: bool) -> &mut Self {
309 self.verbatim_arguments = verbatim;
310 self
311 }
312
313 pub fn get_current_dir(&self) -> Option<&Path> {
314 self.cwd.as_deref().map(Path::new)
315 }
316
317 pub fn current_dir<S: AsRef<Path>>(&mut self, cwd: S) -> &mut Self {
318 self.cwd = Some(cwd.as_ref().to_path_buf().into_os_string());
319 self
320 }
321
322 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
323 self.args.push(arg.as_ref().to_os_string());
324 self
325 }
326
327 pub fn args<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
328 &mut self,
329 args: I,
330 ) -> &mut Self {
331 self
332 .args
333 .extend(args.into_iter().map(|a| a.as_ref().to_os_string()));
334 self
335 }
336
337 pub fn env<S: AsRef<OsStr>, T: AsRef<OsStr>>(
338 &mut self,
339 key: S,
340 value: T,
341 ) -> &mut Self {
342 self.envs.set(key.as_ref(), value.as_ref());
343 self
344 }
345
346 pub fn get_program(&self) -> &OsStr {
347 self.program.as_os_str()
348 }
349
350 pub fn get_args(&self) -> impl Iterator<Item = &OsStr> {
351 self.args.iter().skip(1).map(|a| a.as_os_str())
352 }
353
354 pub fn envs<
355 I: IntoIterator<Item = (S, T)>,
356 S: AsRef<OsStr>,
357 T: AsRef<OsStr>,
358 >(
359 &mut self,
360 envs: I,
361 ) -> &mut Self {
362 for (k, v) in envs {
363 self.envs.set(k.as_ref(), v.as_ref());
364 }
365 self
366 }
367
368 pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Self {
369 self.kill_on_drop = kill_on_drop;
370 self
371 }
372
373 pub fn detached(&mut self) -> &mut Self {
374 self.detached = true;
375 self.kill_on_drop = false;
376 self
377 }
378
379 pub fn env_clear(&mut self) -> &mut Self {
380 self.envs.clear();
381 self
382 }
383
384 pub fn stdin(&mut self, stdin: Stdio) -> &mut Self {
385 self.stdin = stdin;
386 self
387 }
388
389 pub fn stdout(&mut self, stdout: Stdio) -> &mut Self {
390 self.stdout = stdout;
391 self
392 }
393
394 pub fn stderr(&mut self, stderr: Stdio) -> &mut Self {
395 self.stderr = stderr;
396 self
397 }
398
399 pub fn extra_handle(&mut self, handle: Option<HANDLE>) -> &mut Self {
400 self.extra_handles.push(handle);
401 self
402 }
403
404 pub fn spawn(&mut self) -> Result<Child, std::io::Error> {
405 let mut flags = 0;
406 if self.detached {
407 flags |= uv_process_flags::Detached;
408 }
409 if self.verbatim_arguments {
410 flags |= uv_process_flags::WindowsVerbatimArguments;
411 }
412
413 let (stdin, child_stdin) = match self.stdin {
414 Stdio::Pipe => {
415 let pipes = crate::anon_pipe::anon_pipe(false, true)?;
416 let child_stdin_handle = pipes.ours.into_handle();
417 let stdin_handle = pipes.theirs.into_handle().into_raw_handle();
418
419 (
420 StdioContainer::RawHandle(stdin_handle),
421 Some(ChildStdin::from(child_stdin_handle)),
422 )
423 }
424 Stdio::Null => (StdioContainer::Ignore, None),
425 Stdio::Inherit => (StdioContainer::InheritFd(0), None),
426 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
427 };
428 let (stdout, child_stdout) = match self.stdout {
429 Stdio::Pipe => {
430 let pipes = crate::anon_pipe::anon_pipe(true, true)?;
431 let child_stdout_handle = pipes.ours.into_handle();
432 let stdout_handle = pipes.theirs.into_handle().into_raw_handle();
433
434 (
435 StdioContainer::RawHandle(stdout_handle),
436 Some(ChildStdout::from(child_stdout_handle)),
437 )
438 }
439 Stdio::Null => (StdioContainer::Ignore, None),
440 Stdio::Inherit => (StdioContainer::InheritFd(1), None),
441 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
442 };
443 let (stderr, child_stderr) = match self.stderr {
444 Stdio::Pipe => {
445 let pipes = crate::anon_pipe::anon_pipe(true, true)?;
446 let child_stderr_handle = pipes.ours.into_handle();
447 let stderr_handle = pipes.theirs.into_handle().into_raw_handle();
448
449 (
450 StdioContainer::RawHandle(stderr_handle),
451 Some(ChildStderr::from(child_stderr_handle)),
452 )
453 }
454 Stdio::Null => (StdioContainer::Ignore, None),
455 Stdio::Inherit => (StdioContainer::InheritFd(2), None),
456 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
457 };
458
459 let mut stdio = Vec::with_capacity(3 + self.extra_handles.len());
460 stdio.extend([stdin, stdout, stderr]);
461 stdio.extend(self.extra_handles.iter().map(|h| {
462 h.map(StdioContainer::RawHandle)
463 .unwrap_or(StdioContainer::Ignore)
464 }));
465
466 crate::process::spawn(&SpawnOptions {
467 flags,
468 file: Cow::Borrowed(&self.program),
469 args: self
470 .args
471 .iter()
472 .map(|a| Cow::Borrowed(a.as_os_str()))
473 .collect(),
474 env: &self.envs,
475 cwd: self.cwd.as_deref().map(Cow::Borrowed),
476 stdio,
477 })
478 .map_err(|err| std::io::Error::other(err.to_string()))
479 .map(|process| Child {
480 inner: FusedChild::Child(ChildDropGuard {
481 inner: process,
482 kill_on_drop: self.kill_on_drop,
483 }),
484 stdin: child_stdin,
485 stdout: child_stdout,
486 stderr: child_stderr,
487 })
488 }
489}