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::FileType;
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;
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;
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::sync::atomic::AtomicIsize;
114use std::sync::atomic::Ordering;
115use std::sync::atomic::compiler_fence;
116use std::time::Duration;
117use std::time::Instant;
118use yash_executor::Executor;
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 fn file_has_type(&self, path: &CStr, r#type: FileType) -> bool {
251 self.fstatat(AT_FDCWD, path, true)
252 .is_ok_and(|stat| stat.r#type == r#type)
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 fn fstat(&self, fd: Fd) -> Result<Stat> {
269 let mut stat = MaybeUninit::<libc::stat>::uninit();
270 unsafe { libc::fstat(fd.0, stat.as_mut_ptr()) }.errno_if_m1()?;
271 let stat = unsafe { Stat::from_raw(&stat) };
272 Ok(stat)
273 }
274
275 fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat> {
276 let flags = if follow_symlinks {
277 0
278 } else {
279 libc::AT_SYMLINK_NOFOLLOW
280 };
281
282 let mut stat = MaybeUninit::<libc::stat>::uninit();
283 unsafe { libc::fstatat(dir_fd.0, path.as_ptr(), stat.as_mut_ptr(), flags) }
284 .errno_if_m1()?;
285 let stat = unsafe { Stat::from_raw(&stat) };
286 Ok(stat)
287 }
288}
289
290impl IsExecutableFile for RealSystem {
291 fn is_executable_file(&self, path: &CStr) -> bool {
292 self.file_has_type(path, FileType::Regular) && self.has_execute_permission(path)
293 }
294}
295
296impl Pipe for RealSystem {
297 fn pipe(&self) -> Result<(Fd, Fd)> {
298 let mut fds = MaybeUninit::<[c_int; 2]>::uninit();
299 unsafe { libc::pipe(fds.as_mut_ptr().cast()) }.errno_if_m1()?;
301 let fds = unsafe { fds.assume_init() };
302 Ok((Fd(fds[0]), Fd(fds[1])))
303 }
304}
305
306impl Dup for RealSystem {
307 fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
308 let command = if flags.contains(FdFlag::CloseOnExec) {
309 libc::F_DUPFD_CLOEXEC
310 } else {
311 libc::F_DUPFD
312 };
313 unsafe { libc::fcntl(from.0, command, to_min.0) }
314 .errno_if_m1()
315 .map(Fd)
316 }
317
318 fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
319 loop {
320 let result = unsafe { libc::dup2(from.0, to.0) }.errno_if_m1().map(Fd);
321 if result != Err(Errno::EINTR) {
322 return result;
323 }
324 }
325 }
326}
327
328impl Open for RealSystem {
329 fn open(
330 &self,
331 path: &CStr,
332 access: OfdAccess,
333 flags: EnumSet<OpenFlag>,
334 mode: Mode,
335 ) -> Result<Fd> {
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"))]
342 let mode_bits = mode.bits() as std::ffi::c_uint;
343 #[cfg(target_os = "redox")]
344 let mode_bits = mode.bits() as c_int;
345
346 unsafe { libc::open(path.as_ptr(), raw_flags, mode_bits) }
347 .errno_if_m1()
348 .map(Fd)
349 }
350
351 fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd> {
352 let parent_dir = OsStr::from_bytes(parent_dir.as_unix_str().as_bytes());
353 let file = tempfile::tempfile_in(parent_dir)
354 .map_err(|errno| Errno(errno.raw_os_error().unwrap_or(0)))?;
355 let fd = Fd(file.into_raw_fd());
356
357 _ = self.fcntl_setfd(fd, EnumSet::empty());
359
360 Ok(fd)
361 }
362
363 fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
364 let dir = unsafe { libc::fdopendir(fd.0) };
365 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
366 Ok(RealDir(dir))
367 }
368
369 fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
370 let dir = unsafe { libc::opendir(path.as_ptr()) };
371 let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
372 Ok(RealDir(dir))
373 }
374}
375
376impl Close for RealSystem {
377 fn close(&self, fd: Fd) -> Result<()> {
378 loop {
380 let result = unsafe { libc::close(fd.0) }.errno_if_m1().map(drop);
381 match result {
382 Err(Errno::EBADF) => return Ok(()),
383 Err(Errno::EINTR) => continue,
384 other => return other,
385 }
386 }
387 }
388}
389
390impl Fcntl for RealSystem {
391 fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
392 let flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
393 Ok(OfdAccess::from_real_flag(flags))
394 }
395
396 fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
397 let old_flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
398 let new_flags = if nonblocking {
399 old_flags | libc::O_NONBLOCK
400 } else {
401 old_flags & !libc::O_NONBLOCK
402 };
403 if new_flags != old_flags {
404 unsafe { libc::fcntl(fd.0, libc::F_SETFL, new_flags) }.errno_if_m1()?;
405 }
406 let was_nonblocking = old_flags & libc::O_NONBLOCK != 0;
407 Ok(was_nonblocking)
408 }
409
410 fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
411 let bits = unsafe { libc::fcntl(fd.0, libc::F_GETFD) }.errno_if_m1()?;
412 let mut flags = EnumSet::empty();
413 if bits & libc::FD_CLOEXEC != 0 {
414 flags.insert(FdFlag::CloseOnExec);
415 }
416 Ok(flags)
417 }
418
419 fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
420 let mut bits = 0 as c_int;
421 if flags.contains(FdFlag::CloseOnExec) {
422 bits |= libc::FD_CLOEXEC;
423 }
424 unsafe { libc::fcntl(fd.0, libc::F_SETFD, bits) }
425 .errno_if_m1()
426 .map(drop)
427 }
428}
429
430impl Read for RealSystem {
431 fn read(&self, fd: Fd, buffer: &mut [u8]) -> Result<usize> {
432 loop {
433 let result =
434 unsafe { libc::read(fd.0, buffer.as_mut_ptr().cast(), buffer.len()) }.errno_if_m1();
435 if result != Err(Errno::EINTR) {
436 return Ok(result?.try_into().unwrap());
437 }
438 }
439 }
440}
441
442impl Write for RealSystem {
443 fn write(&self, fd: Fd, buffer: &[u8]) -> Result<usize> {
444 loop {
445 let result =
446 unsafe { libc::write(fd.0, buffer.as_ptr().cast(), buffer.len()) }.errno_if_m1();
447 if result != Err(Errno::EINTR) {
448 return Ok(result?.try_into().unwrap());
449 }
450 }
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 ) -> Result<()> {
784 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(
885 &self,
886 readers: &mut Vec<Fd>,
887 writers: &mut Vec<Fd>,
888 timeout: Option<Duration>,
889 signal_mask: Option<&[signal::Number]>,
890 ) -> Result<c_int> {
891 use std::ptr::{null, null_mut};
892
893 let max_fd = readers.iter().chain(writers.iter()).max();
894 let nfds = max_fd
895 .map(|fd| fd.0.checked_add(1).ok_or(Errno::EBADF))
896 .transpose()?
897 .unwrap_or(0);
898
899 fn to_raw_fd_set(fds: &[Fd]) -> MaybeUninit<libc::fd_set> {
900 let mut raw_fds = MaybeUninit::<libc::fd_set>::uninit();
901 unsafe {
902 libc::FD_ZERO(raw_fds.as_mut_ptr());
903 for fd in fds {
904 libc::FD_SET(fd.0, raw_fds.as_mut_ptr());
905 }
906 }
907 raw_fds
908 }
909 let mut raw_readers = to_raw_fd_set(readers);
910 let mut raw_writers = to_raw_fd_set(writers);
911 let readers_ptr = raw_readers.as_mut_ptr();
912 let writers_ptr = raw_writers.as_mut_ptr();
913 let errors = null_mut();
914
915 let timeout_spec = to_timespec(timeout.unwrap_or_default());
916 let timeout_ptr = if timeout.is_some() {
917 timeout_spec.as_ptr()
918 } else {
919 null()
920 };
921
922 let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
923 let raw_mask_ptr = match signal_mask {
924 None => null(),
925 Some(signal_mask) => {
926 unsafe { libc::sigemptyset(raw_mask.as_mut_ptr()) }.errno_if_m1()?;
927 for &signal in signal_mask {
928 unsafe { libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()) }
929 .errno_if_m1()?;
930 }
931 raw_mask.as_ptr()
932 }
933 };
934
935 let count = unsafe {
936 libc::pselect(
937 nfds,
938 readers_ptr,
939 writers_ptr,
940 errors,
941 timeout_ptr,
942 raw_mask_ptr,
943 )
944 }
945 .errno_if_m1()?;
946
947 readers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, readers_ptr) });
948 writers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, writers_ptr) });
949
950 Ok(count)
951 }
952}
953
954impl Isatty for RealSystem {
955 fn isatty(&self, fd: Fd) -> bool {
956 (unsafe { libc::isatty(fd.0) } != 0)
957 }
958}
959
960impl TcGetPgrp for RealSystem {
961 fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
962 unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
963 }
964}
965
966impl TcSetPgrp for RealSystem {
967 fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
968 let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
969 ready(result.errno_if_m1().map(drop))
970 }
971}
972
973impl Fork for RealSystem {
974 fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
982 let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
983 if raw_pid != 0 {
984 return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
986 }
987
988 Ok(Box::new(|env, task| {
990 let system = env.system.clone();
991 let executor = Executor::new();
995 let task = Box::pin(async move { match task(env).await {} });
996 unsafe { executor.spawn_pinned(task) }
999 loop {
1000 executor.run_until_stalled();
1001 system.select(false).ok();
1002 }
1003 }))
1004 }
1005}
1006
1007impl Wait for RealSystem {
1008 fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
1009 let mut status = 0;
1010 let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
1011 match unsafe { libc::waitpid(target.0, &mut status, options) } {
1012 -1 => Err(Errno::last()),
1013 0 => Ok(None),
1014 pid => {
1015 let state = if libc::WIFCONTINUED(status) {
1016 ProcessState::Running
1017 } else if libc::WIFEXITED(status) {
1018 let exit_status = libc::WEXITSTATUS(status);
1019 ProcessState::exited(exit_status)
1020 } else if libc::WIFSIGNALED(status) {
1021 let signal = libc::WTERMSIG(status);
1022 let core_dump = libc::WCOREDUMP(status);
1023 let raw_number = unsafe { NonZero::new_unchecked(signal) };
1025 let signal = signal::Number::from_raw_unchecked(raw_number);
1026 let process_result = ProcessResult::Signaled { signal, core_dump };
1027 process_result.into()
1028 } else if libc::WIFSTOPPED(status) {
1029 let signal = libc::WSTOPSIG(status);
1030 let raw_number = unsafe { NonZero::new_unchecked(signal) };
1032 let signal = signal::Number::from_raw_unchecked(raw_number);
1033 ProcessState::stopped(signal)
1034 } else {
1035 unreachable!()
1036 };
1037 Ok(Some((Pid(pid), state)))
1038 }
1039 }
1040 }
1041}
1042
1043impl Exec for RealSystem {
1044 fn execve(
1045 &self,
1046 path: &CStr,
1047 args: &[CString],
1048 envs: &[CString],
1049 ) -> impl Future<Output = Result<Infallible>> + use<> {
1050 fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
1051 strs.iter()
1052 .map(|s| s.as_ref().as_ptr())
1053 .chain(std::iter::once(std::ptr::null()))
1054 .collect()
1055 }
1056 let args = to_pointer_array(args);
1067 let envs = to_pointer_array(envs);
1068 loop {
1069 let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
1070 let errno = Errno::last();
1071 if errno != Errno::EINTR {
1072 return ready(Err(errno));
1073 }
1074 }
1075 }
1076}
1077
1078impl Exit for RealSystem {
1079 #[allow(unreachable_code)]
1080 fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1081 ready(unsafe { libc::_exit(exit_status.0) })
1082 }
1083}
1084
1085impl GetUid for RealSystem {
1086 fn getuid(&self) -> Uid {
1087 Uid(unsafe { libc::getuid() })
1088 }
1089
1090 fn geteuid(&self) -> Uid {
1091 Uid(unsafe { libc::geteuid() })
1092 }
1093
1094 fn getgid(&self) -> Gid {
1095 Gid(unsafe { libc::getgid() })
1096 }
1097
1098 fn getegid(&self) -> Gid {
1099 Gid(unsafe { libc::getegid() })
1100 }
1101}
1102
1103impl GetPw for RealSystem {
1104 fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1105 Errno::clear();
1106 let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
1107 if passwd.is_null() {
1108 let errno = Errno::last();
1109 return if errno == Errno::NO_ERROR {
1110 Ok(None)
1111 } else {
1112 Err(errno)
1113 };
1114 }
1115
1116 let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
1117 Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
1118 }
1119}
1120
1121impl Sysconf for RealSystem {
1122 fn confstr_path(&self) -> Result<UnixString> {
1123 #[cfg(any(
1125 target_os = "linux",
1126 target_os = "macos",
1127 target_os = "ios",
1128 target_os = "tvos",
1129 target_os = "watchos"
1130 ))]
1131 unsafe {
1132 let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
1133 if size == 0 {
1134 return Err(Errno::last());
1135 }
1136 let mut buffer = Vec::<u8>::with_capacity(size);
1137 let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
1138 if final_size == 0 {
1139 return Err(Errno::last());
1140 }
1141 if final_size > size {
1142 return Err(Errno::ERANGE);
1143 }
1144 buffer.set_len(final_size - 1); return Ok(UnixString::from_vec(buffer));
1146 }
1147
1148 #[allow(unreachable_code)]
1149 Err(Errno::ENOSYS)
1150 }
1151}
1152
1153impl ShellPath for RealSystem {
1154 fn shell_path(&self) -> CString {
1160 #[cfg(any(target_os = "linux", target_os = "android"))]
1161 if self.is_executable_file(c"/proc/self/exe") {
1162 return c"/proc/self/exe".to_owned();
1163 }
1164 if let Ok(path) = self.confstr_path() {
1168 if let Some(full_path) = path
1169 .as_bytes()
1170 .split(|b| *b == b':')
1171 .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
1172 .filter(|full_path| full_path.is_absolute())
1173 .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
1174 .find(|full_path| self.is_executable_file(full_path))
1175 {
1176 return full_path;
1177 }
1178 }
1179
1180 c"/bin/sh".to_owned()
1182 }
1183}
1184
1185impl GetRlimit for RealSystem {
1186 fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1187 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1188
1189 let mut limits = MaybeUninit::<libc::rlimit>::uninit();
1190 unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
1191
1192 Ok(LimitPair {
1196 soft: unsafe { (*limits.as_ptr()).rlim_cur },
1197 hard: unsafe { (*limits.as_ptr()).rlim_max },
1198 })
1199 }
1200}
1201
1202impl SetRlimit for RealSystem {
1203 fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1204 let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1205
1206 let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
1207 unsafe {
1208 (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
1209 (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
1210 }
1211
1212 unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
1213 Ok(())
1214 }
1215}
1216
1217#[derive(Debug)]
1219struct RealDir(NonNull<DIR>);
1220
1221impl Drop for RealDir {
1222 fn drop(&mut self) {
1223 unsafe {
1224 libc::closedir(self.0.as_ptr());
1225 }
1226 }
1227}
1228
1229impl Dir for RealDir {
1230 fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
1231 Errno::clear();
1232 let entry = unsafe { libc::readdir(self.0.as_ptr()) };
1233 let errno = Errno::last();
1234 if entry.is_null() {
1235 if errno == Errno::NO_ERROR {
1236 Ok(None)
1237 } else {
1238 Err(errno)
1239 }
1240 } else {
1241 let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
1243 let name = UnixStr::from_bytes(name.to_bytes());
1244 Ok(Some(DirEntry { name }))
1245 }
1246 }
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251 use super::*;
1252
1253 #[test]
1254 fn real_system_directory_entries() {
1255 let system = unsafe { RealSystem::new() };
1256 let mut dir = system.opendir(c".").unwrap();
1257 let mut count = 0;
1258 while dir.next().unwrap().is_some() {
1259 count += 1;
1260 }
1261 assert!(count > 0);
1262 }
1263
1264 #[test]
1266 fn real_system_caught_signals() {
1267 unsafe {
1268 let system = RealSystem::new();
1269 let result = system.caught_signals();
1270 assert_eq!(result, []);
1271
1272 catch_signal(libc::SIGINT);
1273 catch_signal(libc::SIGTERM);
1274 catch_signal(libc::SIGTERM);
1275 catch_signal(libc::SIGCHLD);
1276
1277 let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
1278 let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
1279 let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
1280
1281 let result = system.caught_signals();
1282 assert_eq!(result, [sigint, sigterm, sigchld]);
1283 let result = system.caught_signals();
1284 assert_eq!(result, []);
1285 }
1286 }
1287}