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