1#[cfg(feature = "alloc")]
2use alloc::boxed::Box;
3#[cfg(feature = "alloc")]
4use alloc::vec;
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7use core::hint::unreachable_unchecked;
8
9use rusl::error::Errno;
10use rusl::platform::{Fd, GidT, OpenFlags, PidT, UidT, WaitPidFlags};
11use rusl::platform::{STDERR, STDIN, STDOUT};
12use rusl::string::unix_str::UnixStr;
13#[cfg(feature = "alloc")]
14use rusl::string::unix_str::UnixString;
15
16use crate::error::{Error, Result};
17use crate::fs::OpenOptions;
18use crate::io::{Read, Write};
19use crate::unix::fd::{BorrowedFd, OwnedFd, RawFd};
20
21const DEV_NULL: &UnixStr = UnixStr::from_str_checked("/dev/null\0");
22
23#[inline]
25pub fn exit(code: i32) -> ! {
26 rusl::process::exit(code)
27}
28
29#[cfg(feature = "alloc")]
30pub struct Command<'a> {
31 bin: &'a UnixStr,
32 args: Vec<&'a UnixStr>,
33 argv: Argv,
34 closures: Vec<Box<dyn FnMut() -> Result<()> + Send + Sync>>,
35 env: Environment,
36 cwd: Option<&'a UnixStr>,
37 uid: Option<UidT>,
38 gid: Option<GidT>,
39 stdin: Option<Stdio>,
40 stdout: Option<Stdio>,
41 stderr: Option<Stdio>,
42 pgroup: Option<PidT>,
43}
44
45#[cfg(feature = "alloc")]
47struct Argv(Vec<*const u8>);
48
49#[cfg(feature = "alloc")]
52unsafe impl Send for Argv {}
53
54#[cfg(feature = "alloc")]
55unsafe impl Sync for Argv {}
56
57#[cfg(feature = "alloc")]
59struct Envp(Vec<*const u8>);
60
61#[cfg(feature = "alloc")]
64unsafe impl Send for Envp {}
65
66#[cfg(feature = "alloc")]
67unsafe impl Sync for Envp {}
68
69#[cfg(feature = "alloc")]
70impl<'a> Command<'a> {
71 pub fn new(bin: &'a UnixStr) -> Result<Self> {
75 let bin_ptr = bin.as_ptr();
76 Ok(Self {
77 bin,
78 args: vec![bin],
79 argv: Argv(vec![bin_ptr, core::ptr::null()]),
80 closures: vec![],
81 env: Environment::default(),
82 cwd: None,
83 uid: None,
84 gid: None,
85 stdin: None,
86 stdout: None,
87 stderr: None,
88 pgroup: None,
89 })
90 }
91
92 pub fn env(&mut self, env: UnixString) -> &mut Self {
95 #[cfg(feature = "start")]
96 if matches!(self.env, Environment::Inherit | Environment::None) {
97 self.env = Environment::Provided(ProvidedEnvironment {
98 vars: vec![],
99 envp: Envp(vec![core::ptr::null()]),
100 });
101 };
102 #[cfg(not(feature = "start"))]
103 if !matches!(self.env, Environment::None) {
104 self.env = Environment::Provided(ProvidedEnvironment {
105 vars: vec![],
106 envp: Envp(vec![core::ptr::null()]),
107 });
108 };
109 if let Environment::Provided(pe) = &mut self.env {
110 let s = env;
111 pe.envp.0[pe.vars.len()] = s.as_ptr();
112 pe.envp.0.push(core::ptr::null());
113 pe.vars.push(s);
114 }
115 self
116 }
117
118 pub fn envs(&mut self, envs: impl Iterator<Item = UnixString>) -> &mut Self {
121 for env in envs {
122 self.env(env);
123 }
124 self
125 }
126
127 pub fn arg(&mut self, arg: &'a UnixStr) -> &mut Self {
130 let unix_string = arg;
131 self.argv.0[self.args.len()] = unix_string.as_ptr();
132 self.argv.0.push(core::ptr::null());
133 self.args.push(unix_string);
134 self
135 }
136
137 pub fn args(&mut self, args: impl Iterator<Item = &'a UnixStr>) -> &mut Self {
140 for arg in args {
141 self.arg(arg);
142 }
143 self
144 }
145
146 pub unsafe fn pre_exec<F: FnMut() -> Result<()> + Send + Sync + 'static>(
151 &mut self,
152 f: F,
153 ) -> &mut Self {
154 self.closures.push(Box::new(f));
155 self
156 }
157
158 pub fn cwd(&mut self, dir: &'a UnixStr) -> &mut Self {
161 self.cwd = Some(dir);
162 self
163 }
164
165 pub fn uid(&mut self, id: UidT) -> &mut Self {
166 self.uid = Some(id);
167 self
168 }
169
170 pub fn gid(&mut self, id: GidT) -> &mut Self {
171 self.gid = Some(id);
172 self
173 }
174
175 pub fn pgroup(&mut self, pgroup: PidT) -> &mut Self {
176 self.pgroup = Some(pgroup);
177 self
178 }
179
180 pub fn stdin(&mut self, stdin: Stdio) -> &mut Self {
181 self.stdin = Some(stdin);
182 self
183 }
184
185 pub fn stdout(&mut self, stdout: Stdio) -> &mut Self {
186 self.stdout = Some(stdout);
187 self
188 }
189
190 pub fn stderr(&mut self, stderr: Stdio) -> &mut Self {
191 self.stderr = Some(stderr);
192 self
193 }
194
195 pub fn spawn(&mut self) -> Result<Child> {
199 const NULL_ENV: [*const u8; 1] = [core::ptr::null()];
200 let envp = match &self.env {
201 #[cfg(feature = "start")]
202 Environment::Inherit => unsafe { crate::env::ENV.env_p },
203 Environment::None => NULL_ENV.as_ptr(),
204 Environment::Provided(provided) => provided.envp.0.as_ptr(),
205 };
206 unsafe {
207 do_spawn(
208 self.bin,
209 self.argv.0.as_ptr(),
210 envp,
211 Stdio::Inherit,
212 true,
213 self.stdin,
214 self.stdout,
215 self.stderr,
216 &mut self.closures,
217 self.cwd,
218 self.uid,
219 self.gid,
220 self.pgroup,
221 )
222 }
223 }
224
225 pub fn exec(&mut self) -> Error {
226 const NULL_ENV: [*const u8; 1] = [core::ptr::null()];
227 let envp = match &self.env {
228 #[cfg(feature = "start")]
229 Environment::Inherit => unsafe { crate::env::ENV.env_p },
230 Environment::None => NULL_ENV.as_ptr(),
231 Environment::Provided(provided) => provided.envp.0.as_ptr(),
232 };
233 unsafe { do_exec(self.bin, self.argv.0.as_ptr(), envp, &mut self.closures) }
234 }
235}
236
237pub struct Child {
238 pub(crate) handle: Process,
239
240 pub stdin: Option<AnonPipe>,
241
242 pub stdout: Option<AnonPipe>,
243
244 pub stderr: Option<AnonPipe>,
245}
246
247impl Child {
248 #[inline]
250 #[must_use]
251 pub fn get_pid(&self) -> i32 {
252 self.handle.pid
253 }
254 #[inline]
258 pub fn wait(&mut self) -> Result<i32> {
259 drop(self.stdin.take());
260 self.handle.wait()
261 }
262
263 #[inline]
268 pub fn try_wait(&mut self) -> Result<Option<i32>> {
269 self.handle.try_wait()
270 }
271}
272
273pub struct Process {
274 pid: i32,
275 status: Option<i32>,
276}
277
278impl Process {
279 fn wait(&mut self) -> Result<i32> {
280 if let Some(status) = self.status {
281 return Ok(status);
282 }
283 let res = rusl::process::wait_pid(self.pid, WaitPidFlags::empty())?;
284 self.status = Some(res.status);
285 Ok(res.status)
286 }
287
288 fn try_wait(&mut self) -> Result<Option<i32>> {
289 if let Some(status) = self.status {
290 return Ok(Some(status));
291 }
292 let res = rusl::process::wait_pid(self.pid, WaitPidFlags::WNOHANG)?;
293 if res.pid == 0 {
294 Ok(None)
295 } else {
296 self.status = Some(res.status);
297 Ok(Some(res.status))
298 }
299 }
300}
301
302#[derive(Debug, Copy, Clone)]
303pub enum Stdio {
304 Inherit,
305 Null,
306 MakePipe,
307 RawFd(Fd),
308}
309
310impl Stdio {
311 fn to_child_stdio(self, readable: bool) -> Result<(ChildStdio, Option<AnonPipe>)> {
312 match self {
313 Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
314
315 Stdio::MakePipe => {
316 let pipe = rusl::unistd::pipe2(OpenFlags::O_CLOEXEC)?;
317 let (ours, theirs) = if readable {
318 (pipe.out_pipe, pipe.in_pipe)
319 } else {
320 (pipe.in_pipe, pipe.out_pipe)
321 };
322 Ok((
323 ChildStdio::Owned(OwnedFd(theirs)),
324 Some(AnonPipe(OwnedFd(ours))),
325 ))
326 }
327
328 Stdio::Null => {
329 let mut opts = OpenOptions::new();
330 opts.read(readable);
331 opts.write(!readable);
332 let fd = opts.open(DEV_NULL)?;
333 Ok((ChildStdio::Owned(fd.into_inner()), None))
334 }
335
336 Stdio::RawFd(fd) => Ok((ChildStdio::Owned(OwnedFd(fd)), None)),
337 }
338 }
339}
340
341pub enum ChildStdio {
342 Inherit,
343 Owned(OwnedFd),
344}
345
346impl ChildStdio {
347 fn fd(&self) -> Option<RawFd> {
348 match self {
349 ChildStdio::Inherit => None,
350 ChildStdio::Owned(fd) => Some(fd.0),
351 }
352 }
353}
354
355#[non_exhaustive]
356pub enum Environment {
357 #[cfg(feature = "start")]
358 Inherit,
359 None,
360 #[cfg(feature = "alloc")]
361 Provided(ProvidedEnvironment),
362}
363
364#[cfg(feature = "alloc")]
365pub struct ProvidedEnvironment {
366 vars: Vec<UnixString>,
367 envp: Envp,
368}
369
370#[expect(clippy::derivable_impls)]
371impl Default for Environment {
372 fn default() -> Self {
373 #[cfg(feature = "start")]
374 {
375 Environment::Inherit
376 }
377 #[cfg(not(feature = "start"))]
378 {
379 Environment::None
380 }
381 }
382}
383
384pub trait PreExec {
385 fn run(&mut self) -> Result<()>;
389}
390
391#[cfg(feature = "alloc")]
392impl PreExec for Box<dyn FnMut() -> Result<()> + Send + Sync> {
393 #[inline]
394 fn run(&mut self) -> Result<()> {
395 (self)()
396 }
397}
398
399impl PreExec for &'_ mut (dyn FnMut() -> Result<()> + Send + Sync) {
400 #[inline]
401 fn run(&mut self) -> Result<()> {
402 (self)()
403 }
404}
405
406impl PreExec for () {
407 #[inline]
408 fn run(&mut self) -> Result<()> {
409 Ok(())
410 }
411}
412
413#[inline]
418pub unsafe fn do_exec<F: PreExec>(
419 bin: &UnixStr,
420 argv: *const *const u8,
421 envp: *const *const u8,
422 closures: &mut [F],
423) -> Error {
424 for closure in closures {
425 if let Err(e) = closure.run() {
426 return e;
427 }
428 }
429 let Err(e) = rusl::process::execve(bin, argv, envp) else {
430 unreachable_unchecked();
432 };
433 e.into()
434}
435
436#[inline]
437#[expect(clippy::too_many_arguments)]
438unsafe fn do_spawn<F: PreExec>(
439 bin: &UnixStr,
440 argv: *const *const u8,
441 envp: *const *const u8,
442 default_stdio: Stdio,
443 needs_stdin: bool,
444 stdin: Option<Stdio>,
445 stdout: Option<Stdio>,
446 stderr: Option<Stdio>,
447 closures: &mut [F],
448 cwd: Option<&UnixStr>,
449 uid: Option<UidT>,
450 gid: Option<GidT>,
451 pgroup: Option<PidT>,
452) -> Result<Child> {
453 const CLOEXEC_MSG_FOOTER: [u8; 4] = *b"NOEX";
454 let (ours, theirs) = setup_io(default_stdio, needs_stdin, stdin, stdout, stderr)?;
455 let sync_pipe = rusl::unistd::pipe2(OpenFlags::O_CLOEXEC)?;
456 let (read_pipe, write_pipe) = (sync_pipe.in_pipe, sync_pipe.out_pipe);
457 let child_pid = rusl::process::fork()?;
458 if child_pid == 0 {
460 let _ = rusl::unistd::close(read_pipe);
462 if let Some(fd) = theirs.stdin.fd() {
463 rusl::unistd::dup2(fd, STDIN)?;
464 }
465 if let Some(fd) = theirs.stdout.fd() {
466 rusl::unistd::dup2(fd, STDOUT)?;
467 }
468 if let Some(fd) = theirs.stderr.fd() {
469 rusl::unistd::dup2(fd, STDERR)?;
470 }
471 if let Some(cwd) = cwd {
472 rusl::unistd::chdir(cwd)?;
473 }
474 if let Some(uid) = uid {
475 rusl::unistd::setuid(uid)?;
476 }
477 if let Some(gid) = gid {
478 rusl::unistd::setgid(gid)?;
479 }
480 if let Some(pgroup) = pgroup {
481 rusl::unistd::setpgid(0, pgroup)?;
482 }
483 for closure in closures {
484 closure.run()?;
485 }
486 let Err(e) = rusl::process::execve(bin, argv, envp) else {
487 unreachable_unchecked();
489 };
490 let code: [u8; 4] = if let Some(code) = e.code {
491 code.raw().to_be_bytes()
492 } else {
493 rusl::process::exit(1)
494 };
495 let bytes = [
496 code[0],
497 code[1],
498 code[2],
499 code[3],
500 CLOEXEC_MSG_FOOTER[0],
501 CLOEXEC_MSG_FOOTER[1],
502 CLOEXEC_MSG_FOOTER[2],
503 CLOEXEC_MSG_FOOTER[3],
504 ];
505 let _ = rusl::unistd::write(write_pipe, &bytes);
506 rusl::process::exit(1);
507 }
508 let _ = rusl::unistd::close(write_pipe);
509 let mut process = Process {
510 pid: child_pid,
511 status: None,
512 };
513 let mut bytes = [0, 0, 0, 0, 0, 0, 0, 0];
514 loop {
515 match rusl::unistd::read(read_pipe, &mut bytes) {
516 Ok(0) => {
517 let child = Child {
518 handle: process,
519 stdin: ours.stdin,
520 stdout: ours.stdout,
521 stderr: ours.stderr,
522 };
523 return Ok(child);
524 }
525 Ok(8) => {
526 let (errno, footer) = bytes.split_at(4);
527 if CLOEXEC_MSG_FOOTER != footer {
528 return Err(Error::no_code("Validation on the CLOEXEC pipe failed"));
529 }
530
531 let errno = Errno::new(i32::from_be_bytes(errno.try_into().unwrap_unchecked()));
532 process.wait()?;
533 return Err(Error::os("Failed to wait for process", errno));
534 }
535 Err(ref e) if matches!(e.code, Some(Errno::EINTR)) => {}
536 Err(_) => {
537 process.wait()?;
538 return Err(Error::no_code("The cloexec pipe failed"));
539 }
540 Ok(..) => {
541 process.wait()?;
543 return Err(Error::no_code("Short read on the CLOEXEC pipe"));
544 }
545 }
546 }
547}
548
549#[cfg(not(feature = "alloc"))]
560#[expect(clippy::too_many_arguments)]
561pub fn spawn<const N: usize, CL: PreExec>(
562 bin: &UnixStr,
563 argv: [&UnixStr; N],
564 env: &Environment,
565 stdin: Option<Stdio>,
566 stdout: Option<Stdio>,
567 stderr: Option<Stdio>,
568 closures: &mut [CL],
569 cwd: Option<&UnixStr>,
570 uid: Option<UidT>,
571 gid: Option<GidT>,
572 pgroup: Option<PidT>,
573) -> Result<Child> {
574 const NO_ENV: [*const u8; 1] = [core::ptr::null()];
575 let mut no_args: [*const u8; 2] = [core::ptr::null_mut(), core::ptr::null_mut()];
576 let envp = match env {
577 #[cfg(feature = "start")]
578 Environment::Inherit => unsafe { crate::env::ENV.env_p },
579 Environment::None => NO_ENV.as_ptr(),
580 };
581 let mut new_args = [core::ptr::null(); N];
582 let arg_ptr = if argv.is_empty() {
583 no_args[0] = bin.as_ptr();
585 no_args.as_ptr()
586 } else {
587 for (ind, arg) in argv.into_iter().enumerate() {
588 new_args[ind] = arg.as_ptr();
589 }
590 new_args[N - 1] = core::ptr::null();
591 new_args.as_ptr()
592 };
593 unsafe {
596 do_spawn(
597 bin,
598 arg_ptr,
599 envp,
600 Stdio::Inherit,
601 true,
602 stdin,
603 stdout,
604 stderr,
605 closures,
606 cwd,
607 uid,
608 gid,
609 pgroup,
610 )
611 }
612}
613
614pub struct AnonPipe(OwnedFd);
615
616impl AnonPipe {
617 #[inline]
618 #[must_use]
619 pub fn borrow_fd(&self) -> BorrowedFd {
620 BorrowedFd::new(self.0 .0)
621 }
622}
623
624impl Read for AnonPipe {
625 #[inline]
626 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
627 Ok(rusl::unistd::read(self.0 .0, buf)?)
628 }
629}
630
631impl Write for AnonPipe {
632 #[inline]
633 fn write(&mut self, buf: &[u8]) -> Result<usize> {
634 Ok(rusl::unistd::write(self.0 .0, buf)?)
635 }
636
637 #[inline]
638 fn flush(&mut self) -> Result<()> {
639 Ok(())
640 }
641}
642
643pub struct StdioPipes {
646 pub stdin: Option<AnonPipe>,
647 pub stdout: Option<AnonPipe>,
648 pub stderr: Option<AnonPipe>,
649}
650
651pub struct ChildPipes {
654 pub stdin: ChildStdio,
655 pub stdout: ChildStdio,
656 pub stderr: ChildStdio,
657}
658
659fn setup_io(
660 default: Stdio,
661 needs_stdin: bool,
662 stdin: Option<Stdio>,
663 stdout: Option<Stdio>,
664 stderr: Option<Stdio>,
665) -> Result<(StdioPipes, ChildPipes)> {
666 let null = Stdio::Null;
667 let default_stdin = if needs_stdin { default } else { null };
668 let stdin = stdin.unwrap_or(default_stdin);
669 let stdout = stdout.unwrap_or(default);
670 let stderr = stderr.unwrap_or(default);
671 let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
672 let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
673 let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
674 let ours = StdioPipes {
675 stdin: our_stdin,
676 stdout: our_stdout,
677 stderr: our_stderr,
678 };
679 let theirs = ChildPipes {
680 stdin: their_stdin,
681 stdout: their_stdout,
682 stderr: their_stderr,
683 };
684 Ok((ours, theirs))
685}