1#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4#![cfg_attr(
5 all(feature = "linux_pidfd", target_os = "linux"),
6 feature(linux_pidfd)
7)]
8#![warn(missing_docs)]
9
10cfg_if::cfg_if! {
11 if #[cfg(windows)] {
12 #[path = "windows.rs"]
13 mod sys;
14 } else if #[cfg(target_os = "linux")] {
15 #[path = "linux.rs"]
16 mod sys;
17 } else {
18 #[path = "unix.rs"]
19 mod sys;
20 }
21}
22
23#[cfg(unix)]
24use std::os::unix::process::CommandExt;
25#[cfg(windows)]
26use std::os::windows::process::CommandExt;
27use std::{ffi::OsStr, io, path::Path, process};
28
29use compio_buf::{BufResult, IntoInner};
30use compio_driver::{AsFd, AsRawFd, BorrowedFd, RawFd, SharedFd, ToSharedFd};
31use compio_io::AsyncReadExt;
32use compio_runtime::Attacher;
33use futures_util::future::Either;
34
35#[derive(Debug)]
109pub struct Command(process::Command);
110
111impl Command {
112 pub fn new(program: impl AsRef<OsStr>) -> Self {
114 Self(process::Command::new(program))
115 }
116
117 pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
119 self.0.arg(arg);
120 self
121 }
122
123 pub fn args<I, S>(&mut self, args: I) -> &mut Self
125 where
126 I: IntoIterator<Item = S>,
127 S: AsRef<OsStr>,
128 {
129 self.0.args(args);
130 self
131 }
132
133 pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
135 where
136 K: AsRef<OsStr>,
137 V: AsRef<OsStr>,
138 {
139 self.0.env(key, val);
140 self
141 }
142
143 pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
145 where
146 I: IntoIterator<Item = (K, V)>,
147 K: AsRef<OsStr>,
148 V: AsRef<OsStr>,
149 {
150 self.0.envs(vars);
151 self
152 }
153
154 pub fn env_remove(&mut self, key: impl AsRef<OsStr>) -> &mut Self {
157 self.0.env_remove(key);
158 self
159 }
160
161 pub fn env_clear(&mut self) -> &mut Self {
164 self.0.env_clear();
165 self
166 }
167
168 pub fn current_dir(&mut self, dir: impl AsRef<Path>) -> &mut Self {
170 self.0.current_dir(dir);
171 self
172 }
173
174 pub fn stdin<S: TryInto<process::Stdio>>(&mut self, cfg: S) -> Result<&mut Self, S::Error> {
176 self.0.stdin(cfg.try_into()?);
177 Ok(self)
178 }
179
180 pub fn stdout<S: TryInto<process::Stdio>>(&mut self, cfg: S) -> Result<&mut Self, S::Error> {
182 self.0.stdout(cfg.try_into()?);
183 Ok(self)
184 }
185
186 pub fn stderr<S: TryInto<process::Stdio>>(&mut self, cfg: S) -> Result<&mut Self, S::Error> {
188 self.0.stderr(cfg.try_into()?);
189 Ok(self)
190 }
191
192 pub fn get_program(&self) -> &OsStr {
194 self.0.get_program()
195 }
196
197 pub fn get_args(&self) -> process::CommandArgs<'_> {
199 self.0.get_args()
200 }
201
202 pub fn get_envs(&self) -> process::CommandEnvs<'_> {
205 self.0.get_envs()
206 }
207
208 pub fn get_current_dir(&self) -> Option<&Path> {
210 self.0.get_current_dir()
211 }
212
213 pub fn spawn(&mut self) -> io::Result<Child> {
215 #[cfg(all(target_os = "linux", feature = "linux_pidfd"))]
216 {
217 use std::os::linux::process::CommandExt;
218 self.0.create_pidfd(true);
219 }
220 let mut child = self.0.spawn()?;
221 let stdin = if let Some(stdin) = child.stdin.take() {
222 Some(ChildStdin::new(stdin)?)
223 } else {
224 None
225 };
226 let stdout = if let Some(stdout) = child.stdout.take() {
227 Some(ChildStdout::new(stdout)?)
228 } else {
229 None
230 };
231 let stderr = if let Some(stderr) = child.stderr.take() {
232 Some(ChildStderr::new(stderr)?)
233 } else {
234 None
235 };
236 Ok(Child {
237 child,
238 stdin,
239 stdout,
240 stderr,
241 })
242 }
243
244 pub async fn status(&mut self) -> io::Result<process::ExitStatus> {
248 let child = self.spawn()?;
249 child.wait().await
250 }
251
252 pub async fn output(&mut self) -> io::Result<process::Output> {
255 let child = self.spawn()?;
256 child.wait_with_output().await
257 }
258}
259
260#[cfg(windows)]
261impl Command {
262 pub fn creation_flags(&mut self, flags: u32) -> &mut Self {
268 self.0.creation_flags(flags);
269 self
270 }
271
272 pub fn raw_arg(&mut self, text_to_append_as_is: impl AsRef<OsStr>) -> &mut Self {
274 self.0.raw_arg(text_to_append_as_is);
275 self
276 }
277}
278
279#[cfg(unix)]
280impl Command {
281 pub fn uid(&mut self, id: u32) -> &mut Self {
285 self.0.uid(id);
286 self
287 }
288
289 pub fn gid(&mut self, id: u32) -> &mut Self {
292 self.0.gid(id);
293 self
294 }
295
296 pub unsafe fn pre_exec(
303 &mut self,
304 f: impl FnMut() -> io::Result<()> + Send + Sync + 'static,
305 ) -> &mut Self {
306 self.0.pre_exec(f);
307 self
308 }
309
310 pub fn exec(&mut self) -> io::Error {
312 self.0.exec()
313 }
314
315 pub fn arg0(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
318 self.0.arg0(arg);
319 self
320 }
321
322 pub fn process_group(&mut self, pgroup: i32) -> &mut Self {
324 self.0.process_group(pgroup);
325 self
326 }
327}
328
329pub struct Child {
347 child: process::Child,
348 pub stdin: Option<ChildStdin>,
350 pub stdout: Option<ChildStdout>,
352 pub stderr: Option<ChildStderr>,
354}
355
356impl Child {
357 pub fn kill(&mut self) -> io::Result<()> {
360 self.child.kill()
361 }
362
363 pub fn id(&self) -> u32 {
365 self.child.id()
366 }
367
368 pub async fn wait(self) -> io::Result<process::ExitStatus> {
373 sys::child_wait(self.child).await
374 }
375
376 pub async fn wait_with_output(mut self) -> io::Result<process::Output> {
379 let status = sys::child_wait(self.child);
380 let stdout = if let Some(stdout) = &mut self.stdout {
381 Either::Left(stdout.read_to_end(vec![]))
382 } else {
383 Either::Right(std::future::ready(BufResult(Ok(0), vec![])))
384 };
385 let stderr = if let Some(stderr) = &mut self.stderr {
386 Either::Left(stderr.read_to_end(vec![]))
387 } else {
388 Either::Right(std::future::ready(BufResult(Ok(0), vec![])))
389 };
390 let (status, BufResult(out_res, stdout), BufResult(err_res, stderr)) =
391 futures_util::future::join3(status, stdout, stderr).await;
392 let status = status?;
393 out_res?;
394 err_res?;
395 Ok(process::Output {
396 status,
397 stdout,
398 stderr,
399 })
400 }
401}
402
403pub struct ChildStdout(Attacher<process::ChildStdout>);
406
407impl ChildStdout {
408 fn new(stdout: process::ChildStdout) -> io::Result<Self> {
409 Attacher::new(stdout).map(Self)
410 }
411}
412
413impl TryFrom<ChildStdout> for process::Stdio {
414 type Error = ChildStdout;
415
416 fn try_from(value: ChildStdout) -> Result<Self, ChildStdout> {
417 value
418 .0
419 .into_inner()
420 .try_unwrap()
421 .map(Self::from)
422 .map_err(|fd| ChildStdout(unsafe { Attacher::from_shared_fd_unchecked(fd) }))
423 }
424}
425
426impl AsFd for ChildStdout {
427 fn as_fd(&self) -> BorrowedFd<'_> {
428 self.0.as_fd()
429 }
430}
431
432impl AsRawFd for ChildStdout {
433 fn as_raw_fd(&self) -> RawFd {
434 self.0.as_raw_fd()
435 }
436}
437
438impl ToSharedFd<process::ChildStdout> for ChildStdout {
439 fn to_shared_fd(&self) -> SharedFd<process::ChildStdout> {
440 self.0.to_shared_fd()
441 }
442}
443
444pub struct ChildStderr(Attacher<process::ChildStderr>);
446
447impl ChildStderr {
448 fn new(stderr: process::ChildStderr) -> io::Result<Self> {
449 Attacher::new(stderr).map(Self)
450 }
451}
452
453impl TryFrom<ChildStderr> for process::Stdio {
454 type Error = ChildStderr;
455
456 fn try_from(value: ChildStderr) -> Result<Self, ChildStderr> {
457 value
458 .0
459 .into_inner()
460 .try_unwrap()
461 .map(Self::from)
462 .map_err(|fd| ChildStderr(unsafe { Attacher::from_shared_fd_unchecked(fd) }))
463 }
464}
465
466impl AsFd for ChildStderr {
467 fn as_fd(&self) -> BorrowedFd<'_> {
468 self.0.as_fd()
469 }
470}
471
472impl AsRawFd for ChildStderr {
473 fn as_raw_fd(&self) -> RawFd {
474 self.0.as_raw_fd()
475 }
476}
477
478impl ToSharedFd<process::ChildStderr> for ChildStderr {
479 fn to_shared_fd(&self) -> SharedFd<process::ChildStderr> {
480 self.0.to_shared_fd()
481 }
482}
483
484pub struct ChildStdin(Attacher<process::ChildStdin>);
487
488impl ChildStdin {
489 fn new(stdin: process::ChildStdin) -> io::Result<Self> {
490 Attacher::new(stdin).map(Self)
491 }
492}
493
494impl TryFrom<ChildStdin> for process::Stdio {
495 type Error = ChildStdin;
496
497 fn try_from(value: ChildStdin) -> Result<Self, ChildStdin> {
498 value
499 .0
500 .into_inner()
501 .try_unwrap()
502 .map(Self::from)
503 .map_err(|fd| ChildStdin(unsafe { Attacher::from_shared_fd_unchecked(fd) }))
504 }
505}
506
507impl AsFd for ChildStdin {
508 fn as_fd(&self) -> BorrowedFd<'_> {
509 self.0.as_fd()
510 }
511}
512
513impl AsRawFd for ChildStdin {
514 fn as_raw_fd(&self) -> RawFd {
515 self.0.as_raw_fd()
516 }
517}
518
519impl ToSharedFd<process::ChildStdin> for ChildStdin {
520 fn to_shared_fd(&self) -> SharedFd<process::ChildStdin> {
521 self.0.to_shared_fd()
522 }
523}