1mod errno;
25mod fd_set;
26mod file_system;
27mod open_flag;
28mod resource;
29mod signal;
30
31use super::AT_FDCWD;
32use super::CaughtSignals;
33use super::Chdir;
34use super::ChildProcessStarter;
35use super::Clock;
36use super::Close;
37use super::CpuTimes;
38use super::Dir;
39use super::DirEntry;
40use super::Disposition;
41use super::Dup;
42use super::Errno;
43use super::Exec;
44use super::Exit;
45use super::Fcntl;
46use super::FdFlag;
47use super::Fork;
48use super::Fstat;
49use super::GetCwd;
50use super::GetPid;
51use super::GetPw;
52use super::GetRlimit;
53use super::GetSigaction;
54use super::GetUid;
55use super::Gid;
56use super::IsExecutableFile;
57use super::Isatty;
58use super::Mode;
59use super::OfdAccess;
60use super::Open;
61use super::OpenFlag;
62use super::Pipe;
63use super::Read;
64use super::Result;
65use super::Seek;
66use super::Select;
67use super::SendSignal;
68use super::SetPgid;
69use super::SetRlimit;
70use super::ShellPath;
71use super::Sigaction;
72use super::Sigmask;
73use super::SigmaskOp;
74use super::Signals;
75use super::Stat as _;
76use super::Sysconf;
77use super::TcGetPgrp;
78use super::TcSetPgrp;
79use super::Times;
80use super::Uid;
81use super::Umask;
82use super::Wait;
83use super::Write;
84use super::resource::LimitPair;
85use super::resource::Resource;
86#[cfg(doc)]
87use crate::Env;
88use crate::io::Fd;
89use crate::job::Pid;
90use crate::job::ProcessResult;
91use crate::job::ProcessState;
92use crate::path::Path;
93use crate::path::PathBuf;
94use crate::semantics::ExitStatus;
95use crate::str::UnixStr;
96use crate::str::UnixString;
97use enumset::EnumSet;
98pub use fd_set::FdSet;
99pub use file_system::Stat;
100use libc::DIR;
101pub use signal::Sigset;
102use std::convert::Infallible;
103use std::convert::TryInto as _;
104use std::ffi::CStr;
105use std::ffi::CString;
106use std::ffi::OsStr;
107use std::ffi::c_int;
108use std::future::ready;
109use std::io::SeekFrom;
110use std::mem::MaybeUninit;
111use std::num::NonZero;
112use std::ops::RangeInclusive;
113use std::os::unix::ffi::OsStrExt as _;
114use std::os::unix::io::IntoRawFd as _;
115use std::pin::pin;
116use std::ptr::NonNull;
117use std::rc::Rc;
118use std::sync::atomic::AtomicIsize;
119use std::sync::atomic::Ordering;
120use std::sync::atomic::compiler_fence;
121use std::task::Context;
122use std::task::Waker;
123use std::time::Duration;
124use std::time::Instant;
125
126trait ErrnoIfM1: PartialEq + Sized {
127 const MINUS_1: Self;
128
129 fn errno_if_m1(self) -> Result<Self> {
137 if self == Self::MINUS_1 {
138 Err(Errno::last())
139 } else {
140 Ok(self)
141 }
142 }
143}
144
145impl ErrnoIfM1 for i8 {
146 const MINUS_1: Self = -1;
147}
148impl ErrnoIfM1 for i16 {
149 const MINUS_1: Self = -1;
150}
151impl ErrnoIfM1 for i32 {
152 const MINUS_1: Self = -1;
153}
154impl ErrnoIfM1 for i64 {
155 const MINUS_1: Self = -1;
156}
157impl ErrnoIfM1 for isize {
158 const MINUS_1: Self = -1;
159}
160
161#[must_use]
166fn to_timespec(duration: Duration) -> MaybeUninit<libc::timespec> {
167 let seconds = duration.as_secs().try_into().unwrap_or(libc::time_t::MAX);
168 let mut timespec = MaybeUninit::<libc::timespec>::uninit();
169 unsafe {
170 (&raw mut (*timespec.as_mut_ptr()).tv_sec).write(seconds);
171 (&raw mut (*timespec.as_mut_ptr()).tv_nsec).write(duration.subsec_nanos() as _);
172 }
173 timespec
174}
175
176static CAUGHT_SIGNALS: [AtomicIsize; 8] = [const { AtomicIsize::new(0) }; 8];
182
183extern "C" fn catch_signal(signal: c_int) {
189 let signal = signal as isize;
197 for slot in &CAUGHT_SIGNALS {
198 match slot.compare_exchange(0, signal, Ordering::Relaxed, Ordering::Relaxed) {
199 Ok(_) => break,
200 Err(slot_value) if slot_value == signal => break,
201 _ => continue,
202 }
203 }
204}
205
206fn sigaction_impl(signal: signal::Number, disposition: Option<Disposition>) -> Result<Disposition> {
207 let new_action = disposition.map(Disposition::to_sigaction);
208 let new_action_ptr = new_action
209 .as_ref()
210 .map_or(std::ptr::null(), |action| action.as_ptr());
211
212 let mut old_action = MaybeUninit::<libc::sigaction>::uninit();
213 let old_mask_ptr = unsafe { &raw mut (*old_action.as_mut_ptr()).sa_mask };
216 unsafe { libc::sigemptyset(old_mask_ptr) }.errno_if_m1()?;
219
220 unsafe { libc::sigaction(signal.as_raw(), new_action_ptr, old_action.as_mut_ptr()) }
221 .errno_if_m1()?;
222
223 let old_disposition = unsafe { Disposition::from_sigaction(&old_action) };
225 Ok(old_disposition)
226}
227
228#[derive(Debug)]
239pub struct RealSystem(());
240
241impl RealSystem {
242 pub unsafe fn new() -> Self {
253 RealSystem(())
254 }
255
256 #[cfg(not(target_os = "redox"))]
258 fn has_execute_permission(&self, path: &CStr) -> bool {
259 (unsafe { libc::faccessat(libc::AT_FDCWD, path.as_ptr(), libc::X_OK, libc::AT_EACCESS) })
260 != -1
261 }
262 #[cfg(target_os = "redox")]
263 fn has_execute_permission(&self, path: &CStr) -> bool {
264 (unsafe { libc::access(path.as_ptr(), libc::X_OK) }) != -1
265 }
266}
267
268impl Fstat for RealSystem {
269 type Stat = file_system::Stat;
270
271 fn fstat(&self, fd: Fd) -> Result<file_system::Stat> {
272 let mut stat = MaybeUninit::<libc::stat>::uninit();
273 unsafe { libc::fstat(fd.0, stat.as_mut_ptr()) }.errno_if_m1()?;
274 let stat = unsafe { file_system::Stat::from_raw(stat) };
275 Ok(stat)
276 }
277
278 fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<file_system::Stat> {
279 let flags = if follow_symlinks {
280 0
281 } else {
282 libc::AT_SYMLINK_NOFOLLOW
283 };
284
285 let mut stat = MaybeUninit::<libc::stat>::uninit();
286 unsafe { libc::fstatat(dir_fd.0, path.as_ptr(), stat.as_mut_ptr(), flags) }
287 .errno_if_m1()?;
288 let stat = unsafe { file_system::Stat::from_raw(stat) };
289 Ok(stat)
290 }
291}
292
293impl IsExecutableFile for RealSystem {
294 fn is_executable_file(&self, path: &CStr) -> bool {
295 self.fstatat(AT_FDCWD, path, true)
296 .is_ok_and(|stat| stat.is_regular_file())
297 && self.has_execute_permission(path)
298 }
299}
300
301impl Pipe for RealSystem {
302 fn pipe(&self) -> Result<(Fd, Fd)> {
303 let mut fds = MaybeUninit::<[c_int; 2]>::uninit();
304 unsafe { libc::pipe(fds.as_mut_ptr().cast()) }.errno_if_m1()?;
306 let fds = unsafe { fds.assume_init() };
307 Ok((Fd(fds[0]), Fd(fds[1])))
308 }
309}
310
311impl Dup for RealSystem {
312 fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
313 let command = if flags.contains(FdFlag::CloseOnExec) {
314 libc::F_DUPFD_CLOEXEC
315 } else {
316 libc::F_DUPFD
317 };
318 unsafe { libc::fcntl(from.0, command, to_min.0) }
319 .errno_if_m1()
320 .map(Fd)
321 }
322
323 fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
324 loop {
325 let result = unsafe { libc::dup2(from.0, to.0) }.errno_if_m1().map(Fd);
326 if result != Err(Errno::EINTR) {
327 return result;
328 }
329 }
330 }
331}
332
333impl Open for RealSystem {
334 fn open(
335 &self,
336 path: &CStr,
337 access: OfdAccess,
338 flags: EnumSet<OpenFlag>,
339 mode: Mode,
340 ) -> impl Future<Output = Result<Fd>> + use<> {
341 ready((|| {
342 let mut raw_flags = access.to_real_flag().ok_or(Errno::EINVAL)?;
343 for flag in flags {
344 raw_flags |= flag.to_real_flag().ok_or(Errno::EINVAL)?;
345 }
346
347 #[cfg(not(target_os = "redox"))]
350 let mode_bits = mode.bits() as std::ffi::c_uint;
351 #[cfg(target_os = "redox")]
352 let mode_bits = mode.bits() as c_int;
353
354 let result = unsafe { libc::open(path.as_ptr(), raw_flags, mode_bits) };
355 result.errno_if_m1().map(Fd)
356 })())
357 }
358
359 fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd> {
360 let parent_dir = OsStr::from_bytes(parent_dir.as_unix_str().as_bytes());
361 let file = tempfile::tempfile_in(parent_dir)
362 .map_err(|errno| Errno(errno.raw_os_error().unwrap_or(0)))?;
363 let fd = Fd(file.into_raw_fd());
364
365 _ = self.fcntl_setfd(fd, EnumSet::empty());
367
368 Ok(fd)
369 }
370
371 fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
372 let dir = unsafe { libc::fdopendir(fd.0) };
373 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
374 Ok(RealDir(dir))
375 }
376
377 fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
378 let dir = unsafe { libc::opendir(path.as_ptr()) };
379 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
380 Ok(RealDir(dir))
381 }
382}
383
384impl Close for RealSystem {
385 fn close(&self, fd: Fd) -> Result<()> {
386 loop {
388 let result = unsafe { libc::close(fd.0) }.errno_if_m1().map(drop);
389 match result {
390 Err(Errno::EBADF) => return Ok(()),
391 Err(Errno::EINTR) => continue,
392 other => return other,
393 }
394 }
395 }
396}
397
398impl Fcntl for RealSystem {
399 fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
400 let flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
401 Ok(OfdAccess::from_real_flag(flags))
402 }
403
404 fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
405 let old_flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
406 let new_flags = if nonblocking {
407 old_flags | libc::O_NONBLOCK
408 } else {
409 old_flags & !libc::O_NONBLOCK
410 };
411 if new_flags != old_flags {
412 unsafe { libc::fcntl(fd.0, libc::F_SETFL, new_flags) }.errno_if_m1()?;
413 }
414 let was_nonblocking = old_flags & libc::O_NONBLOCK != 0;
415 Ok(was_nonblocking)
416 }
417
418 fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
419 let bits = unsafe { libc::fcntl(fd.0, libc::F_GETFD) }.errno_if_m1()?;
420 let mut flags = EnumSet::empty();
421 if bits & libc::FD_CLOEXEC != 0 {
422 flags.insert(FdFlag::CloseOnExec);
423 }
424 Ok(flags)
425 }
426
427 fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
428 let mut bits = 0 as c_int;
429 if flags.contains(FdFlag::CloseOnExec) {
430 bits |= libc::FD_CLOEXEC;
431 }
432 unsafe { libc::fcntl(fd.0, libc::F_SETFD, bits) }
433 .errno_if_m1()
434 .map(drop)
435 }
436}
437
438impl Read for RealSystem {
439 fn read<'a>(
441 &self,
442 fd: Fd,
443 buffer: &'a mut [u8],
444 ) -> impl Future<Output = Result<usize>> + use<'a> {
445 let result =
446 unsafe { libc::read(fd.0, buffer.as_mut_ptr().cast(), buffer.len()) }.errno_if_m1();
447 ready(result.map(|len| len.try_into().unwrap()))
448 }
449}
450
451impl Write for RealSystem {
452 fn write<'a>(&self, fd: Fd, buffer: &'a [u8]) -> impl Future<Output = Result<usize>> + use<'a> {
454 let result =
455 unsafe { libc::write(fd.0, buffer.as_ptr().cast(), buffer.len()) }.errno_if_m1();
456 ready(result.map(|len| len.try_into().unwrap()))
457 }
458}
459
460impl Seek for RealSystem {
461 fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
462 let (offset, whence) = match position {
463 SeekFrom::Start(offset) => {
464 let offset = offset.try_into().map_err(|_| Errno::EOVERFLOW)?;
465 (offset, libc::SEEK_SET)
466 }
467 SeekFrom::End(offset) => (offset, libc::SEEK_END),
468 SeekFrom::Current(offset) => (offset, libc::SEEK_CUR),
469 };
470 let new_offset = unsafe { libc::lseek(fd.0, offset, whence) }.errno_if_m1()?;
471 Ok(new_offset.try_into().unwrap())
472 }
473}
474
475impl Umask for RealSystem {
476 fn umask(&self, new_mask: Mode) -> Mode {
477 Mode::from_bits_retain(unsafe { libc::umask(new_mask.bits()) })
478 }
479}
480
481impl GetCwd for RealSystem {
482 fn getcwd(&self) -> Result<PathBuf> {
483 let mut buffer = Vec::<u8>::new();
490 for capacity in [1 << 10, 1 << 12, 1 << 14, 1 << 16] {
491 buffer.reserve_exact(capacity);
492
493 let result = unsafe { libc::getcwd(buffer.as_mut_ptr().cast(), capacity) };
494 if !result.is_null() {
495 let len = unsafe { CStr::from_ptr(buffer.as_ptr().cast()) }.count_bytes();
497 unsafe { buffer.set_len(len) }
498 buffer.shrink_to_fit();
499 return Ok(PathBuf::from(UnixString::from_vec(buffer)));
500 }
501 let errno = Errno::last();
502 if errno != Errno::ERANGE {
503 return Err(errno);
504 }
505 }
506 Err(Errno::ERANGE)
507 }
508}
509
510impl Chdir for RealSystem {
511 fn chdir(&self, path: &CStr) -> Result<()> {
512 let result = unsafe { libc::chdir(path.as_ptr()) };
513 result.errno_if_m1().map(drop)
514 }
515}
516
517impl Clock for RealSystem {
518 fn now(&self) -> Instant {
519 Instant::now()
520 }
521}
522
523impl Times for RealSystem {
524 fn times(&self) -> Result<CpuTimes> {
529 let mut usage = MaybeUninit::<libc::rusage>::uninit();
530
531 unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) }.errno_if_m1()?;
532 let self_user = unsafe {
533 (*usage.as_ptr()).ru_utime.tv_sec as f64
534 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
535 };
536 let self_system = unsafe {
537 (*usage.as_ptr()).ru_stime.tv_sec as f64
538 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
539 };
540
541 unsafe { libc::getrusage(libc::RUSAGE_CHILDREN, usage.as_mut_ptr()) }.errno_if_m1()?;
542 let children_user = unsafe {
543 (*usage.as_ptr()).ru_utime.tv_sec as f64
544 + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
545 };
546 let children_system = unsafe {
547 (*usage.as_ptr()).ru_stime.tv_sec as f64
548 + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
549 };
550
551 Ok(CpuTimes {
552 self_user,
553 self_system,
554 children_user,
555 children_system,
556 })
557 }
558}
559
560impl GetPid for RealSystem {
561 fn getsid(&self, pid: Pid) -> Result<Pid> {
562 unsafe { libc::getsid(pid.0) }.errno_if_m1().map(Pid)
563 }
564
565 fn getpid(&self) -> Pid {
566 Pid(unsafe { libc::getpid() })
567 }
568
569 fn getppid(&self) -> Pid {
570 Pid(unsafe { libc::getppid() })
571 }
572
573 fn getpgrp(&self) -> Pid {
574 Pid(unsafe { libc::getpgrp() })
575 }
576}
577
578impl SetPgid for RealSystem {
579 fn setpgid(&self, pid: Pid, pgid: Pid) -> Result<()> {
580 let result = unsafe { libc::setpgid(pid.0, pgid.0) };
581 result.errno_if_m1().map(drop)
582 }
583}
584
585const fn to_signal_number(sig: c_int) -> signal::Number {
586 signal::Number::from_raw_unchecked(NonZero::new(sig).unwrap())
587}
588
589impl Signals for RealSystem {
590 const SIGABRT: signal::Number = to_signal_number(libc::SIGABRT);
591 const SIGALRM: signal::Number = to_signal_number(libc::SIGALRM);
592 const SIGBUS: signal::Number = to_signal_number(libc::SIGBUS);
593 const SIGCHLD: signal::Number = to_signal_number(libc::SIGCHLD);
594 #[cfg(any(
595 target_os = "aix",
596 target_os = "horizon",
597 target_os = "illumos",
598 target_os = "solaris",
599 ))]
600 const SIGCLD: Option<signal::Number> = Some(to_signal_number(libc::SIGCLD));
601 #[cfg(not(any(
602 target_os = "aix",
603 target_os = "horizon",
604 target_os = "illumos",
605 target_os = "solaris",
606 )))]
607 const SIGCLD: Option<signal::Number> = None;
608 const SIGCONT: signal::Number = to_signal_number(libc::SIGCONT);
609 #[cfg(not(any(
610 target_os = "android",
611 target_os = "emscripten",
612 target_os = "fuchsia",
613 target_os = "haiku",
614 target_os = "linux",
615 target_os = "redox",
616 )))]
617 const SIGEMT: Option<signal::Number> = Some(to_signal_number(libc::SIGEMT));
618 #[cfg(any(
619 target_os = "android",
620 target_os = "emscripten",
621 target_os = "fuchsia",
622 target_os = "haiku",
623 target_os = "linux",
624 target_os = "redox",
625 ))]
626 const SIGEMT: Option<signal::Number> = None;
627 const SIGFPE: signal::Number = to_signal_number(libc::SIGFPE);
628 const SIGHUP: signal::Number = to_signal_number(libc::SIGHUP);
629 const SIGILL: signal::Number = to_signal_number(libc::SIGILL);
630 #[cfg(not(any(
631 target_os = "aix",
632 target_os = "android",
633 target_os = "emscripten",
634 target_os = "fuchsia",
635 target_os = "haiku",
636 target_os = "linux",
637 target_os = "redox",
638 )))]
639 const SIGINFO: Option<signal::Number> = Some(to_signal_number(libc::SIGINFO));
640 #[cfg(any(
641 target_os = "aix",
642 target_os = "android",
643 target_os = "emscripten",
644 target_os = "fuchsia",
645 target_os = "haiku",
646 target_os = "linux",
647 target_os = "redox",
648 ))]
649 const SIGINFO: Option<signal::Number> = None;
650 const SIGINT: signal::Number = to_signal_number(libc::SIGINT);
651 #[cfg(any(
652 target_os = "aix",
653 target_os = "android",
654 target_os = "emscripten",
655 target_os = "fuchsia",
656 target_os = "horizon",
657 target_os = "illumos",
658 target_os = "linux",
659 target_os = "nto",
660 target_os = "solaris",
661 ))]
662 const SIGIO: Option<signal::Number> = Some(to_signal_number(libc::SIGIO));
663 #[cfg(not(any(
664 target_os = "aix",
665 target_os = "android",
666 target_os = "emscripten",
667 target_os = "fuchsia",
668 target_os = "horizon",
669 target_os = "illumos",
670 target_os = "linux",
671 target_os = "nto",
672 target_os = "solaris",
673 )))]
674 const SIGIO: Option<signal::Number> = None;
675 const SIGIOT: signal::Number = to_signal_number(libc::SIGIOT);
676 const SIGKILL: signal::Number = to_signal_number(libc::SIGKILL);
677 #[cfg(target_os = "horizon")]
678 const SIGLOST: Option<signal::Number> = Some(to_signal_number(libc::SIGLOST));
679 #[cfg(not(target_os = "horizon"))]
680 const SIGLOST: Option<signal::Number> = None;
681 const SIGPIPE: signal::Number = to_signal_number(libc::SIGPIPE);
682 #[cfg(any(
683 target_os = "aix",
684 target_os = "android",
685 target_os = "emscripten",
686 target_os = "fuchsia",
687 target_os = "haiku",
688 target_os = "horizon",
689 target_os = "illumos",
690 target_os = "linux",
691 target_os = "nto",
692 target_os = "solaris",
693 ))]
694 const SIGPOLL: Option<signal::Number> = Some(to_signal_number(libc::SIGPOLL));
695 #[cfg(not(any(
696 target_os = "aix",
697 target_os = "android",
698 target_os = "emscripten",
699 target_os = "fuchsia",
700 target_os = "haiku",
701 target_os = "horizon",
702 target_os = "illumos",
703 target_os = "linux",
704 target_os = "nto",
705 target_os = "solaris",
706 )))]
707 const SIGPOLL: Option<signal::Number> = None;
708 const SIGPROF: signal::Number = to_signal_number(libc::SIGPROF);
709 #[cfg(any(
710 target_os = "aix",
711 target_os = "android",
712 target_os = "emscripten",
713 target_os = "fuchsia",
714 target_os = "illumos",
715 target_os = "linux",
716 target_os = "nto",
717 target_os = "redox",
718 target_os = "solaris",
719 ))]
720 const SIGPWR: Option<signal::Number> = Some(to_signal_number(libc::SIGPWR));
721 #[cfg(not(any(
722 target_os = "aix",
723 target_os = "android",
724 target_os = "emscripten",
725 target_os = "fuchsia",
726 target_os = "illumos",
727 target_os = "linux",
728 target_os = "nto",
729 target_os = "redox",
730 target_os = "solaris",
731 )))]
732 const SIGPWR: Option<signal::Number> = None;
733 const SIGQUIT: signal::Number = to_signal_number(libc::SIGQUIT);
734 const SIGSEGV: signal::Number = to_signal_number(libc::SIGSEGV);
735 #[cfg(all(
736 any(
737 target_os = "android",
738 target_os = "emscripten",
739 target_os = "fuchsia",
740 target_os = "linux"
741 ),
742 not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))
743 ))]
744 const SIGSTKFLT: Option<signal::Number> = Some(to_signal_number(libc::SIGSTKFLT));
745 #[cfg(not(all(
746 any(
747 target_os = "android",
748 target_os = "emscripten",
749 target_os = "fuchsia",
750 target_os = "linux"
751 ),
752 not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))
753 )))]
754 const SIGSTKFLT: Option<signal::Number> = None;
755 const SIGSTOP: signal::Number = to_signal_number(libc::SIGSTOP);
756 const SIGSYS: signal::Number = to_signal_number(libc::SIGSYS);
757 const SIGTERM: signal::Number = to_signal_number(libc::SIGTERM);
758 #[cfg(target_os = "freebsd")]
759 const SIGTHR: Option<signal::Number> = Some(to_signal_number(libc::SIGTHR));
760 #[cfg(not(target_os = "freebsd"))]
761 const SIGTHR: Option<signal::Number> = None;
762 const SIGTRAP: signal::Number = to_signal_number(libc::SIGTRAP);
763 const SIGTSTP: signal::Number = to_signal_number(libc::SIGTSTP);
764 const SIGTTIN: signal::Number = to_signal_number(libc::SIGTTIN);
765 const SIGTTOU: signal::Number = to_signal_number(libc::SIGTTOU);
766 const SIGURG: signal::Number = to_signal_number(libc::SIGURG);
767 const SIGUSR1: signal::Number = to_signal_number(libc::SIGUSR1);
768 const SIGUSR2: signal::Number = to_signal_number(libc::SIGUSR2);
769 const SIGVTALRM: signal::Number = to_signal_number(libc::SIGVTALRM);
770 const SIGWINCH: signal::Number = to_signal_number(libc::SIGWINCH);
771 const SIGXCPU: signal::Number = to_signal_number(libc::SIGXCPU);
772 const SIGXFSZ: signal::Number = to_signal_number(libc::SIGXFSZ);
773
774 fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
775 let raw_range = signal::rt_range();
776 let start = signal::Number::from_raw_unchecked(NonZero::new(*raw_range.start())?);
777 let end = signal::Number::from_raw_unchecked(NonZero::new(*raw_range.end())?);
778 Some(start..=end).filter(|range| !range.is_empty())
779 }
780
781 }
783
784impl Sigmask for RealSystem {
785 type Sigset = Sigset;
786
787 fn sigmask(
788 &self,
789 op: Option<(SigmaskOp, &Sigset)>,
790 old_mask: Option<&mut Sigset>,
791 ) -> impl Future<Output = Result<()>> + use<> {
792 ready(unsafe {
793 let (how, raw_mask) = match op {
794 None => (libc::SIG_BLOCK, std::ptr::null()),
795 Some((op, mask)) => {
796 let how = match op {
797 SigmaskOp::Add => libc::SIG_BLOCK,
798 SigmaskOp::Remove => libc::SIG_UNBLOCK,
799 SigmaskOp::Set => libc::SIG_SETMASK,
800 };
801 (how, mask.0.as_ptr())
802 }
803 };
804 let raw_old_mask =
805 old_mask.map_or(std::ptr::null_mut(), |old_mask| old_mask.0.as_mut_ptr());
806
807 libc::sigprocmask(how, raw_mask, raw_old_mask)
808 .errno_if_m1()
809 .map(drop)
810 })
811 }
812}
813
814impl GetSigaction for RealSystem {
815 fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
816 sigaction_impl(signal, None)
817 }
818}
819
820impl Sigaction for RealSystem {
821 fn sigaction(&self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
822 sigaction_impl(signal, Some(handling))
823 }
824}
825
826impl CaughtSignals for RealSystem {
827 fn caught_signals(&self) -> Vec<signal::Number> {
828 let mut signals = Vec::new();
829 for slot in &CAUGHT_SIGNALS {
830 compiler_fence(Ordering::Acquire);
832
833 let number = slot.swap(0, Ordering::Relaxed);
834 let Some(number) = NonZero::new(number as signal::RawNumber) else {
835 break;
838 };
839 signals.push(signal::Number::from_raw_unchecked(number));
840 }
841 signals
842 }
843}
844
845impl SendSignal for RealSystem {
846 fn kill(
847 &self,
848 target: Pid,
849 signal: Option<signal::Number>,
850 ) -> impl Future<Output = Result<()>> + use<> {
851 let raw = signal.map_or(0, signal::Number::as_raw);
852 let result = unsafe { libc::kill(target.0, raw) }.errno_if_m1().map(drop);
853 ready(result)
854 }
855
856 fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
857 let raw = signal.as_raw();
858 let result = unsafe { libc::raise(raw) }.errno_if_m1().map(drop);
859 ready(result)
860 }
861}
862
863impl Select for RealSystem {
864 type FdSet = FdSet;
865
866 fn select<'a>(
867 &self,
868 readers: &'a mut FdSet,
869 writers: &'a mut FdSet,
870 timeout: Option<Duration>,
871 signal_mask: Option<&Sigset>,
872 ) -> impl Future<Output = Result<c_int>> + use<'a> {
873 ready({
874 use std::ptr::{null, null_mut};
875
876 let upper_bound = readers.upper_bound().max(writers.upper_bound());
877 let readers_ptr = readers.as_mut_ptr();
878 let writers_ptr = writers.as_mut_ptr();
879 let errors = null_mut();
880
881 let timeout_spec = timeout.map(to_timespec);
882 let timeout_ptr = timeout_spec.as_ref().map_or(null(), |spec| spec.as_ptr());
883
884 let raw_mask_ptr = signal_mask.map_or(null(), |mask| mask.0.as_ptr());
885
886 unsafe {
887 libc::pselect(
888 upper_bound.0,
889 readers_ptr,
890 writers_ptr,
891 errors,
892 timeout_ptr,
893 raw_mask_ptr,
894 )
895 }
896 .errno_if_m1()
897 })
898 }
899}
900
901impl Isatty for RealSystem {
902 fn isatty(&self, fd: Fd) -> bool {
903 (unsafe { libc::isatty(fd.0) } != 0)
904 }
905}
906
907impl TcGetPgrp for RealSystem {
908 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
909 unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
910 }
911}
912
913impl TcSetPgrp for RealSystem {
914 fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
915 let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
916 ready(result.errno_if_m1().map(drop))
917 }
918}
919
920impl Fork for RealSystem {
921 fn run_in_child_process<D, F>(&self, shared_data: D, child_task: F) -> (Result<Pid>, D)
929 where
930 D: Clone + 'static,
931 F: AsyncFnOnce(Self, D) + 'static,
932 {
933 match unsafe { libc::fork() }.errno_if_m1() {
934 Ok(0) => {} Ok(raw_pid) => return (Ok(Pid(raw_pid)), shared_data), Err(e) => return (Err(e), shared_data),
937 }
938
939 let child_task = pin!(child_task(RealSystem(()), shared_data));
941 let mut context = Context::from_waker(Waker::noop());
942 _ = child_task.poll(&mut context);
943 panic!("child task running in RealSystem should not return");
944 }
945
946 fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
954 let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
955 if raw_pid != 0 {
956 return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
958 }
959
960 Ok(Box::new(|env, task| {
962 let system = Rc::clone(&env.system);
963 match system.run_real(task(env)) {}
964 }))
965 }
966}
967
968impl Wait for RealSystem {
969 fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
970 let mut status = 0;
971 let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
972 match unsafe { libc::waitpid(target.0, &mut status, options) } {
973 -1 => Err(Errno::last()),
974 0 => Ok(None),
975 pid => {
976 let state = if libc::WIFCONTINUED(status) {
977 ProcessState::Running
978 } else if libc::WIFEXITED(status) {
979 let exit_status = libc::WEXITSTATUS(status);
980 ProcessState::exited(exit_status)
981 } else if libc::WIFSIGNALED(status) {
982 let signal = libc::WTERMSIG(status);
983 let core_dump = libc::WCOREDUMP(status);
984 let raw_number = unsafe { NonZero::new_unchecked(signal) };
986 let signal = signal::Number::from_raw_unchecked(raw_number);
987 let process_result = ProcessResult::Signaled { signal, core_dump };
988 process_result.into()
989 } else if libc::WIFSTOPPED(status) {
990 let signal = libc::WSTOPSIG(status);
991 let raw_number = unsafe { NonZero::new_unchecked(signal) };
993 let signal = signal::Number::from_raw_unchecked(raw_number);
994 ProcessState::stopped(signal)
995 } else {
996 unreachable!()
997 };
998 Ok(Some((Pid(pid), state)))
999 }
1000 }
1001 }
1002}
1003
1004impl Exec for RealSystem {
1005 fn execve(
1006 &self,
1007 path: &CStr,
1008 args: &[CString],
1009 envs: &[CString],
1010 ) -> impl Future<Output = Result<Infallible>> + use<> {
1011 fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
1012 strs.iter()
1013 .map(|s| s.as_ref().as_ptr())
1014 .chain(std::iter::once(std::ptr::null()))
1015 .collect()
1016 }
1017 let args = to_pointer_array(args);
1028 let envs = to_pointer_array(envs);
1029 loop {
1030 let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
1031 let errno = Errno::last();
1032 if errno != Errno::EINTR {
1033 return ready(Err(errno));
1034 }
1035 }
1036 }
1037}
1038
1039impl Exit for RealSystem {
1040 fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1041 (unsafe { libc::_exit(exit_status.0) }) as std::future::Ready<Infallible>
1042 }
1043}
1044
1045impl GetUid for RealSystem {
1046 fn getuid(&self) -> Uid {
1047 Uid(unsafe { libc::getuid() })
1048 }
1049
1050 fn geteuid(&self) -> Uid {
1051 Uid(unsafe { libc::geteuid() })
1052 }
1053
1054 fn getgid(&self) -> Gid {
1055 Gid(unsafe { libc::getgid() })
1056 }
1057
1058 fn getegid(&self) -> Gid {
1059 Gid(unsafe { libc::getegid() })
1060 }
1061}
1062
1063impl GetPw for RealSystem {
1064 fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1065 Errno::clear();
1066 let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
1067 if passwd.is_null() {
1068 let errno = Errno::last();
1069 return if errno == Errno::NO_ERROR {
1070 Ok(None)
1071 } else {
1072 Err(errno)
1073 };
1074 }
1075
1076 let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
1077 Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
1078 }
1079}
1080
1081impl Sysconf for RealSystem {
1082 fn confstr_path(&self) -> Result<UnixString> {
1083 #[cfg(any(
1085 target_os = "linux",
1086 target_os = "macos",
1087 target_os = "ios",
1088 target_os = "tvos",
1089 target_os = "watchos"
1090 ))]
1091 unsafe {
1092 let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
1093 if size == 0 {
1094 return Err(Errno::last());
1095 }
1096 let mut buffer = Vec::<u8>::with_capacity(size);
1097 let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
1098 if final_size == 0 {
1099 return Err(Errno::last());
1100 }
1101 if final_size > size {
1102 return Err(Errno::ERANGE);
1103 }
1104 buffer.set_len(final_size - 1); return Ok(UnixString::from_vec(buffer));
1106 }
1107
1108 #[allow(unreachable_code, reason = "for readability")] Err(Errno::ENOSYS)
1110 }
1111}
1112
1113impl ShellPath for RealSystem {
1114 fn shell_path(&self) -> CString {
1120 #[cfg(any(target_os = "linux", target_os = "android"))]
1121 if self.is_executable_file(c"/proc/self/exe") {
1122 return c"/proc/self/exe".to_owned();
1123 }
1124 if let Ok(path) = self.confstr_path() {
1128 if let Some(full_path) = path
1129 .as_bytes()
1130 .split(|b| *b == b':')
1131 .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
1132 .filter(|full_path| full_path.is_absolute())
1133 .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
1134 .find(|full_path| self.is_executable_file(full_path))
1135 {
1136 return full_path;
1137 }
1138 }
1139
1140 c"/bin/sh".to_owned()
1142 }
1143}
1144
1145impl GetRlimit for RealSystem {
1146 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1147 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1148
1149 let mut limits = MaybeUninit::<libc::rlimit>::uninit();
1150 unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
1151
1152 Ok(LimitPair {
1156 soft: unsafe { (*limits.as_ptr()).rlim_cur },
1157 hard: unsafe { (*limits.as_ptr()).rlim_max },
1158 })
1159 }
1160}
1161
1162impl SetRlimit for RealSystem {
1163 fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1164 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1165
1166 let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
1167 unsafe {
1168 (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
1169 (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
1170 }
1171
1172 unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
1173 Ok(())
1174 }
1175}
1176
1177#[derive(Debug)]
1179struct RealDir(NonNull<DIR>);
1180
1181impl Drop for RealDir {
1182 fn drop(&mut self) {
1183 unsafe {
1184 libc::closedir(self.0.as_ptr());
1185 }
1186 }
1187}
1188
1189impl Dir for RealDir {
1190 fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
1191 Errno::clear();
1192 let entry = unsafe { libc::readdir(self.0.as_ptr()) };
1193 let errno = Errno::last();
1194 if entry.is_null() {
1195 if errno == Errno::NO_ERROR {
1196 Ok(None)
1197 } else {
1198 Err(errno)
1199 }
1200 } else {
1201 let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
1203 let name = UnixStr::from_bytes(name.to_bytes());
1204 Ok(Some(DirEntry { name }))
1205 }
1206 }
1207}
1208
1209#[cfg(test)]
1210mod tests {
1211 use super::*;
1212
1213 #[test]
1214 fn real_system_directory_entries() {
1215 let system = unsafe { RealSystem::new() };
1216 let mut dir = system.opendir(c".").unwrap();
1217 let mut count = 0;
1218 while dir.next().unwrap().is_some() {
1219 count += 1;
1220 }
1221 assert!(count > 0);
1222 }
1223
1224 #[test]
1226 fn real_system_caught_signals() {
1227 unsafe {
1228 let system = RealSystem::new();
1229 let result = system.caught_signals();
1230 assert_eq!(result, []);
1231
1232 catch_signal(libc::SIGINT);
1233 catch_signal(libc::SIGTERM);
1234 catch_signal(libc::SIGTERM);
1235 catch_signal(libc::SIGCHLD);
1236
1237 let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
1238 let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
1239 let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
1240
1241 let result = system.caught_signals();
1242 assert_eq!(result, [sigint, sigterm, sigchld]);
1243 let result = system.caught_signals();
1244 assert_eq!(result, []);
1245 }
1246 }
1247}