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