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 windows_hide: bool,
290}
291
292impl Command {
293 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
294 Self {
295 program: program.as_ref().to_os_string(),
296 args: vec![program.as_ref().to_os_string()],
297 envs: CommandEnv::default(),
298 detached: false,
299 cwd: None,
300 stdin: Stdio::Inherit,
301 stdout: Stdio::Inherit,
302 stderr: Stdio::Inherit,
303 extra_handles: vec![],
304 kill_on_drop: false,
305 verbatim_arguments: false,
306 windows_hide: false,
307 }
308 }
309
310 pub fn verbatim_arguments(&mut self, verbatim: bool) -> &mut Self {
311 self.verbatim_arguments = verbatim;
312 self
313 }
314
315 pub fn windows_hide(&mut self, hide: bool) -> &mut Self {
316 self.windows_hide = hide;
317 self
318 }
319
320 pub fn get_current_dir(&self) -> Option<&Path> {
321 self.cwd.as_deref().map(Path::new)
322 }
323
324 pub fn current_dir<S: AsRef<Path>>(&mut self, cwd: S) -> &mut Self {
325 self.cwd = Some(cwd.as_ref().to_path_buf().into_os_string());
326 self
327 }
328
329 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
330 self.args.push(arg.as_ref().to_os_string());
331 self
332 }
333
334 pub fn args<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
335 &mut self,
336 args: I,
337 ) -> &mut Self {
338 self
339 .args
340 .extend(args.into_iter().map(|a| a.as_ref().to_os_string()));
341 self
342 }
343
344 pub fn env<S: AsRef<OsStr>, T: AsRef<OsStr>>(
345 &mut self,
346 key: S,
347 value: T,
348 ) -> &mut Self {
349 self.envs.set(key.as_ref(), value.as_ref());
350 self
351 }
352
353 pub fn get_program(&self) -> &OsStr {
354 self.program.as_os_str()
355 }
356
357 pub fn get_args(&self) -> impl Iterator<Item = &OsStr> {
358 self.args.iter().skip(1).map(|a| a.as_os_str())
359 }
360
361 pub fn envs<
362 I: IntoIterator<Item = (S, T)>,
363 S: AsRef<OsStr>,
364 T: AsRef<OsStr>,
365 >(
366 &mut self,
367 envs: I,
368 ) -> &mut Self {
369 for (k, v) in envs {
370 self.envs.set(k.as_ref(), v.as_ref());
371 }
372 self
373 }
374
375 pub fn kill_on_drop(&mut self, kill_on_drop: bool) -> &mut Self {
376 self.kill_on_drop = kill_on_drop;
377 self
378 }
379
380 pub fn detached(&mut self) -> &mut Self {
381 self.detached = true;
382 self.kill_on_drop = false;
383 self
384 }
385
386 pub fn env_clear(&mut self) -> &mut Self {
387 self.envs.clear();
388 self
389 }
390
391 pub fn stdin(&mut self, stdin: Stdio) -> &mut Self {
392 self.stdin = stdin;
393 self
394 }
395
396 pub fn stdout(&mut self, stdout: Stdio) -> &mut Self {
397 self.stdout = stdout;
398 self
399 }
400
401 pub fn stderr(&mut self, stderr: Stdio) -> &mut Self {
402 self.stderr = stderr;
403 self
404 }
405
406 pub fn extra_handle(&mut self, handle: Option<HANDLE>) -> &mut Self {
407 self.extra_handles.push(handle);
408 self
409 }
410
411 pub fn spawn(&mut self) -> Result<Child, std::io::Error> {
412 let mut flags = 0;
413 if self.detached {
414 flags |= uv_process_flags::Detached;
415 }
416 if self.verbatim_arguments {
417 flags |= uv_process_flags::WindowsVerbatimArguments;
418 }
419 if self.windows_hide {
420 flags |= uv_process_flags::WindowsHide;
421 }
422
423 let (stdin, child_stdin) = match self.stdin {
424 Stdio::Pipe => {
425 let pipes = crate::anon_pipe::anon_pipe(false, true)?;
426 let child_stdin_handle = pipes.ours.into_handle();
427 let stdin_handle = pipes.theirs.into_handle().into_raw_handle();
428
429 (
430 StdioContainer::RawHandle(stdin_handle),
431 Some(ChildStdin::from(child_stdin_handle)),
432 )
433 }
434 Stdio::Null => (StdioContainer::Ignore, None),
435 Stdio::Inherit => (StdioContainer::InheritFd(0), None),
436 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
437 };
438 let (stdout, child_stdout) = match self.stdout {
439 Stdio::Pipe => {
440 let pipes = crate::anon_pipe::anon_pipe(true, true)?;
441 let child_stdout_handle = pipes.ours.into_handle();
442 let stdout_handle = pipes.theirs.into_handle().into_raw_handle();
443
444 (
445 StdioContainer::RawHandle(stdout_handle),
446 Some(ChildStdout::from(child_stdout_handle)),
447 )
448 }
449 Stdio::Null => (StdioContainer::Ignore, None),
450 Stdio::Inherit => (StdioContainer::InheritFd(1), None),
451 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
452 };
453 let (stderr, child_stderr) = match self.stderr {
454 Stdio::Pipe => {
455 let pipes = crate::anon_pipe::anon_pipe(true, true)?;
456 let child_stderr_handle = pipes.ours.into_handle();
457 let stderr_handle = pipes.theirs.into_handle().into_raw_handle();
458
459 (
460 StdioContainer::RawHandle(stderr_handle),
461 Some(ChildStderr::from(child_stderr_handle)),
462 )
463 }
464 Stdio::Null => (StdioContainer::Ignore, None),
465 Stdio::Inherit => (StdioContainer::InheritFd(2), None),
466 Stdio::RawHandle(handle) => (StdioContainer::RawHandle(handle), None),
467 };
468
469 let mut stdio = Vec::with_capacity(3 + self.extra_handles.len());
470 stdio.extend([stdin, stdout, stderr]);
471 stdio.extend(self.extra_handles.iter().map(|h| {
472 h.map(StdioContainer::RawHandle)
473 .unwrap_or(StdioContainer::Ignore)
474 }));
475
476 crate::process::spawn(&SpawnOptions {
477 flags,
478 file: Cow::Borrowed(&self.program),
479 args: self
480 .args
481 .iter()
482 .map(|a| Cow::Borrowed(a.as_os_str()))
483 .collect(),
484 env: &self.envs,
485 cwd: self.cwd.as_deref().map(Cow::Borrowed),
486 stdio,
487 })
488 .map(|process| Child {
489 inner: FusedChild::Child(ChildDropGuard {
490 inner: process,
491 kill_on_drop: self.kill_on_drop,
492 }),
493 stdin: child_stdin,
494 stdout: child_stdout,
495 stderr: child_stderr,
496 })
497 }
498}