Skip to main content

yash_env/system/
real.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2021 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Implementation of system traits that actually interacts with the underlying system
18//!
19//! This module is implemented on Unix-like targets only. It provides [`RealSystem`],
20//! an implementation of system traits that interact with the underlying
21//! operating system. This implementation is intended to be used for the actual
22//! shell environment.
23
24mod errno;
25mod fd_set;
26mod file_system;
27mod open_flag;
28mod resource;
29mod signal;
30
31use super::AT_FDCWD;
32use super::CaughtSignals;
33use super::Chdir;
34use super::ChildProcessStarter;
35use super::Clock;
36use super::Close;
37use super::CpuTimes;
38use super::Dir;
39use super::DirEntry;
40use super::Disposition;
41use super::Dup;
42use super::Errno;
43use super::Exec;
44use super::Exit;
45use super::Fcntl;
46use super::FdFlag;
47use super::Fork;
48use super::Fstat;
49use super::GetCwd;
50use super::GetPid;
51use super::GetPw;
52use super::GetRlimit;
53use super::GetSigaction;
54use super::GetUid;
55use super::Gid;
56use super::IsExecutableFile;
57use super::Isatty;
58use super::Mode;
59use super::OfdAccess;
60use super::Open;
61use super::OpenFlag;
62use super::Pipe;
63use super::Read;
64use super::Result;
65use super::Seek;
66use super::Select;
67use super::SendSignal;
68use super::SetPgid;
69use super::SetRlimit;
70use super::ShellPath;
71use super::Sigaction;
72use super::Sigmask;
73use super::SigmaskOp;
74use super::Signals;
75use super::Stat as _;
76use super::Sysconf;
77use super::TcGetPgrp;
78use super::TcSetPgrp;
79use super::Times;
80use super::Uid;
81use super::Umask;
82use super::Wait;
83use super::Write;
84use super::resource::LimitPair;
85use super::resource::Resource;
86#[cfg(doc)]
87use crate::Env;
88use crate::io::Fd;
89use crate::job::Pid;
90use crate::job::ProcessResult;
91use crate::job::ProcessState;
92use crate::path::Path;
93use crate::path::PathBuf;
94use crate::semantics::ExitStatus;
95use crate::str::UnixStr;
96use crate::str::UnixString;
97use enumset::EnumSet;
98pub use fd_set::FdSet;
99pub use file_system::Stat;
100use libc::DIR;
101pub use signal::Sigset;
102use std::convert::Infallible;
103use std::convert::TryInto as _;
104use std::ffi::CStr;
105use std::ffi::CString;
106use std::ffi::OsStr;
107use std::ffi::c_int;
108use std::future::ready;
109use std::io::SeekFrom;
110use std::mem::MaybeUninit;
111use std::num::NonZero;
112use std::ops::RangeInclusive;
113use std::os::unix::ffi::OsStrExt as _;
114use std::os::unix::io::IntoRawFd as _;
115use std::pin::pin;
116use std::ptr::NonNull;
117use std::rc::Rc;
118use std::sync::atomic::AtomicIsize;
119use std::sync::atomic::Ordering;
120use std::sync::atomic::compiler_fence;
121use std::task::Context;
122use std::task::Waker;
123use std::time::Duration;
124use std::time::Instant;
125
126trait ErrnoIfM1: PartialEq + Sized {
127    const MINUS_1: Self;
128
129    /// Convenience function to convert a result of -1 to an `Error` with the
130    /// current `errno`.
131    ///
132    /// This function is intended to be used just after calling a function that
133    /// returns -1 on error and sets `errno` to the error number. This function
134    /// filters out the `-1` result and returns an error containing the current
135    /// `errno`.
136    fn errno_if_m1(self) -> Result<Self> {
137        if self == Self::MINUS_1 {
138            Err(Errno::last())
139        } else {
140            Ok(self)
141        }
142    }
143}
144
145impl ErrnoIfM1 for i8 {
146    const MINUS_1: Self = -1;
147}
148impl ErrnoIfM1 for i16 {
149    const MINUS_1: Self = -1;
150}
151impl ErrnoIfM1 for i32 {
152    const MINUS_1: Self = -1;
153}
154impl ErrnoIfM1 for i64 {
155    const MINUS_1: Self = -1;
156}
157impl ErrnoIfM1 for isize {
158    const MINUS_1: Self = -1;
159}
160
161/// Converts a `Duration` to a `timespec`.
162///
163/// The return value is a `MaybeUninit` because the `timespec` struct may have
164/// padding or extension fields that are not initialized by this function.
165#[must_use]
166fn to_timespec(duration: Duration) -> MaybeUninit<libc::timespec> {
167    let seconds = duration.as_secs().try_into().unwrap_or(libc::time_t::MAX);
168    let mut timespec = MaybeUninit::<libc::timespec>::uninit();
169    unsafe {
170        (&raw mut (*timespec.as_mut_ptr()).tv_sec).write(seconds);
171        (&raw mut (*timespec.as_mut_ptr()).tv_nsec).write(duration.subsec_nanos() as _);
172    }
173    timespec
174}
175
176/// Array of slots to store caught signals.
177///
178/// This array is used to store caught signals. All slots are initialized with
179/// 0, which indicates that the slot is available. When a signal is caught, the
180/// signal number is written into one of unoccupied slots.
181static CAUGHT_SIGNALS: [AtomicIsize; 8] = [const { AtomicIsize::new(0) }; 8];
182
183/// Signal catching function.
184///
185/// This function is set as a signal handler for all signals that the shell
186/// wants to catch. When a signal is caught, the signal number is written into
187/// one of the slots in [`CAUGHT_SIGNALS`].
188extern "C" fn catch_signal(signal: c_int) {
189    // This function can only perform async-signal-safe operations.
190    // Performing unsafe operations is undefined behavior!
191
192    // Find an unused slot (having a value of 0) in CAUGHT_SIGNALS and write the
193    // signal number into it.
194    // If there is a slot having a value of the signal already, do nothing.
195    // If there is no available slot, the signal will be lost!
196    let signal = signal as isize;
197    for slot in &CAUGHT_SIGNALS {
198        match slot.compare_exchange(0, signal, Ordering::Relaxed, Ordering::Relaxed) {
199            Ok(_) => break,
200            Err(slot_value) if slot_value == signal => break,
201            _ => continue,
202        }
203    }
204}
205
206fn sigaction_impl(signal: signal::Number, disposition: Option<Disposition>) -> Result<Disposition> {
207    let new_action = disposition.map(Disposition::to_sigaction);
208    let new_action_ptr = new_action
209        .as_ref()
210        .map_or(std::ptr::null(), |action| action.as_ptr());
211
212    let mut old_action = MaybeUninit::<libc::sigaction>::uninit();
213    // SAFETY: We're just creating a new raw pointer to the struct field.
214    // Nothing is dereferenced.
215    let old_mask_ptr = unsafe { &raw mut (*old_action.as_mut_ptr()).sa_mask };
216    // POSIX requires *all* sigset_t objects to be initialized before use,
217    // even if they are output parameters.
218    unsafe { libc::sigemptyset(old_mask_ptr) }.errno_if_m1()?;
219
220    unsafe { libc::sigaction(signal.as_raw(), new_action_ptr, old_action.as_mut_ptr()) }
221        .errno_if_m1()?;
222
223    // SAFETY: the `old_action` has been initialized by `sigaction`.
224    let old_disposition = unsafe { Disposition::from_sigaction(&old_action) };
225    Ok(old_disposition)
226}
227
228/// Implementation of system traits that actually interact with the underlying system
229///
230/// `RealSystem` is an empty `struct` because the underlying operating system
231/// manages the system's internal state.
232///
233/// Some trait methods implemented by `RealSystem` (such as [`SendSignal::kill`])
234/// return futures, but the returned futures do not perform asynchronous operations.
235/// Those methods block the current thread until the underlying system calls
236/// complete and then return ready futures.
237/// See also the [`system` module](super) documentation.
238#[derive(Debug)]
239pub struct RealSystem(());
240
241impl RealSystem {
242    /// Returns an instance of `RealSystem`.
243    ///
244    /// # Safety
245    ///
246    /// This function is marked `unsafe` because improper use of `RealSystem`
247    /// may lead to undefined behavior. Remember that most operations performed
248    /// on the system by [`Env`] are not thread-safe. You should never use
249    /// `RealSystem` in a multi-threaded program, and it is your responsibility
250    /// to make sure you are using only one instance of `ReadSystem` in the
251    /// process.
252    pub unsafe fn new() -> Self {
253        RealSystem(())
254    }
255
256    // TODO Should use AT_EACCESS on all platforms
257    #[cfg(not(target_os = "redox"))]
258    fn has_execute_permission(&self, path: &CStr) -> bool {
259        (unsafe { libc::faccessat(libc::AT_FDCWD, path.as_ptr(), libc::X_OK, libc::AT_EACCESS) })
260            != -1
261    }
262    #[cfg(target_os = "redox")]
263    fn has_execute_permission(&self, path: &CStr) -> bool {
264        (unsafe { libc::access(path.as_ptr(), libc::X_OK) }) != -1
265    }
266}
267
268impl Fstat for RealSystem {
269    type Stat = file_system::Stat;
270
271    fn fstat(&self, fd: Fd) -> Result<file_system::Stat> {
272        let mut stat = MaybeUninit::<libc::stat>::uninit();
273        unsafe { libc::fstat(fd.0, stat.as_mut_ptr()) }.errno_if_m1()?;
274        let stat = unsafe { file_system::Stat::from_raw(stat) };
275        Ok(stat)
276    }
277
278    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<file_system::Stat> {
279        let flags = if follow_symlinks {
280            0
281        } else {
282            libc::AT_SYMLINK_NOFOLLOW
283        };
284
285        let mut stat = MaybeUninit::<libc::stat>::uninit();
286        unsafe { libc::fstatat(dir_fd.0, path.as_ptr(), stat.as_mut_ptr(), flags) }
287            .errno_if_m1()?;
288        let stat = unsafe { file_system::Stat::from_raw(stat) };
289        Ok(stat)
290    }
291}
292
293impl IsExecutableFile for RealSystem {
294    fn is_executable_file(&self, path: &CStr) -> bool {
295        self.fstatat(AT_FDCWD, path, true)
296            .is_ok_and(|stat| stat.is_regular_file())
297            && self.has_execute_permission(path)
298    }
299}
300
301impl Pipe for RealSystem {
302    fn pipe(&self) -> Result<(Fd, Fd)> {
303        let mut fds = MaybeUninit::<[c_int; 2]>::uninit();
304        // TODO Use as_mut_ptr rather than cast when array_ptr_get is stabilized
305        unsafe { libc::pipe(fds.as_mut_ptr().cast()) }.errno_if_m1()?;
306        let fds = unsafe { fds.assume_init() };
307        Ok((Fd(fds[0]), Fd(fds[1])))
308    }
309}
310
311impl Dup for RealSystem {
312    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
313        let command = if flags.contains(FdFlag::CloseOnExec) {
314            libc::F_DUPFD_CLOEXEC
315        } else {
316            libc::F_DUPFD
317        };
318        unsafe { libc::fcntl(from.0, command, to_min.0) }
319            .errno_if_m1()
320            .map(Fd)
321    }
322
323    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
324        loop {
325            let result = unsafe { libc::dup2(from.0, to.0) }.errno_if_m1().map(Fd);
326            if result != Err(Errno::EINTR) {
327                return result;
328            }
329        }
330    }
331}
332
333impl Open for RealSystem {
334    fn open(
335        &self,
336        path: &CStr,
337        access: OfdAccess,
338        flags: EnumSet<OpenFlag>,
339        mode: Mode,
340    ) -> impl Future<Output = Result<Fd>> + use<> {
341        ready((|| {
342            let mut raw_flags = access.to_real_flag().ok_or(Errno::EINVAL)?;
343            for flag in flags {
344                raw_flags |= flag.to_real_flag().ok_or(Errno::EINVAL)?;
345            }
346
347            // Rust does not perform default argument promotion for C variadic
348            // functions, so we need to promote manually.
349            #[cfg(not(target_os = "redox"))]
350            let mode_bits = mode.bits() as std::ffi::c_uint;
351            #[cfg(target_os = "redox")]
352            let mode_bits = mode.bits() as c_int;
353
354            let result = unsafe { libc::open(path.as_ptr(), raw_flags, mode_bits) };
355            result.errno_if_m1().map(Fd)
356        })())
357    }
358
359    fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd> {
360        let parent_dir = OsStr::from_bytes(parent_dir.as_unix_str().as_bytes());
361        let file = tempfile::tempfile_in(parent_dir)
362            .map_err(|errno| Errno(errno.raw_os_error().unwrap_or(0)))?;
363        let fd = Fd(file.into_raw_fd());
364
365        // Clear the CLOEXEC flag
366        _ = self.fcntl_setfd(fd, EnumSet::empty());
367
368        Ok(fd)
369    }
370
371    fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<>> {
372        let dir = unsafe { libc::fdopendir(fd.0) };
373        let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
374        Ok(RealDir(dir))
375    }
376
377    fn opendir(&self, path: &CStr) -> Result<impl Dir + use<>> {
378        let dir = unsafe { libc::opendir(path.as_ptr()) };
379        let dir = NonNull::new(dir).ok_or_else(Errno::last)?;
380        Ok(RealDir(dir))
381    }
382}
383
384impl Close for RealSystem {
385    fn close(&self, fd: Fd) -> Result<()> {
386        // TODO: Use posix_close when available
387        loop {
388            let result = unsafe { libc::close(fd.0) }.errno_if_m1().map(drop);
389            match result {
390                Err(Errno::EBADF) => return Ok(()),
391                Err(Errno::EINTR) => continue,
392                other => return other,
393            }
394        }
395    }
396}
397
398impl Fcntl for RealSystem {
399    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
400        let flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
401        Ok(OfdAccess::from_real_flag(flags))
402    }
403
404    fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
405        let old_flags = unsafe { libc::fcntl(fd.0, libc::F_GETFL) }.errno_if_m1()?;
406        let new_flags = if nonblocking {
407            old_flags | libc::O_NONBLOCK
408        } else {
409            old_flags & !libc::O_NONBLOCK
410        };
411        if new_flags != old_flags {
412            unsafe { libc::fcntl(fd.0, libc::F_SETFL, new_flags) }.errno_if_m1()?;
413        }
414        let was_nonblocking = old_flags & libc::O_NONBLOCK != 0;
415        Ok(was_nonblocking)
416    }
417
418    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
419        let bits = unsafe { libc::fcntl(fd.0, libc::F_GETFD) }.errno_if_m1()?;
420        let mut flags = EnumSet::empty();
421        if bits & libc::FD_CLOEXEC != 0 {
422            flags.insert(FdFlag::CloseOnExec);
423        }
424        Ok(flags)
425    }
426
427    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
428        let mut bits = 0 as c_int;
429        if flags.contains(FdFlag::CloseOnExec) {
430            bits |= libc::FD_CLOEXEC;
431        }
432        unsafe { libc::fcntl(fd.0, libc::F_SETFD, bits) }
433            .errno_if_m1()
434            .map(drop)
435    }
436}
437
438impl Read for RealSystem {
439    /// Reads data from the file descriptor into the buffer.
440    fn read<'a>(
441        &self,
442        fd: Fd,
443        buffer: &'a mut [u8],
444    ) -> impl Future<Output = Result<usize>> + use<'a> {
445        let result =
446            unsafe { libc::read(fd.0, buffer.as_mut_ptr().cast(), buffer.len()) }.errno_if_m1();
447        ready(result.map(|len| len.try_into().unwrap()))
448    }
449}
450
451impl Write for RealSystem {
452    /// Writes data from the buffer to the file descriptor.
453    fn write<'a>(&self, fd: Fd, buffer: &'a [u8]) -> impl Future<Output = Result<usize>> + use<'a> {
454        let result =
455            unsafe { libc::write(fd.0, buffer.as_ptr().cast(), buffer.len()) }.errno_if_m1();
456        ready(result.map(|len| len.try_into().unwrap()))
457    }
458}
459
460impl Seek for RealSystem {
461    fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
462        let (offset, whence) = match position {
463            SeekFrom::Start(offset) => {
464                let offset = offset.try_into().map_err(|_| Errno::EOVERFLOW)?;
465                (offset, libc::SEEK_SET)
466            }
467            SeekFrom::End(offset) => (offset, libc::SEEK_END),
468            SeekFrom::Current(offset) => (offset, libc::SEEK_CUR),
469        };
470        let new_offset = unsafe { libc::lseek(fd.0, offset, whence) }.errno_if_m1()?;
471        Ok(new_offset.try_into().unwrap())
472    }
473}
474
475impl Umask for RealSystem {
476    fn umask(&self, new_mask: Mode) -> Mode {
477        Mode::from_bits_retain(unsafe { libc::umask(new_mask.bits()) })
478    }
479}
480
481impl GetCwd for RealSystem {
482    fn getcwd(&self) -> Result<PathBuf> {
483        // Some getcwd implementations allocate a buffer for the path if the
484        // first argument is null, but we cannot use that feature because Vec's
485        // allocator may not be compatible with the system's allocator.
486
487        // Since there is no way to know the required buffer size, we try
488        // several buffer sizes.
489        let mut buffer = Vec::<u8>::new();
490        for capacity in [1 << 10, 1 << 12, 1 << 14, 1 << 16] {
491            buffer.reserve_exact(capacity);
492
493            let result = unsafe { libc::getcwd(buffer.as_mut_ptr().cast(), capacity) };
494            if !result.is_null() {
495                // len does not include the null terminator
496                let len = unsafe { CStr::from_ptr(buffer.as_ptr().cast()) }.count_bytes();
497                unsafe { buffer.set_len(len) }
498                buffer.shrink_to_fit();
499                return Ok(PathBuf::from(UnixString::from_vec(buffer)));
500            }
501            let errno = Errno::last();
502            if errno != Errno::ERANGE {
503                return Err(errno);
504            }
505        }
506        Err(Errno::ERANGE)
507    }
508}
509
510impl Chdir for RealSystem {
511    fn chdir(&self, path: &CStr) -> Result<()> {
512        let result = unsafe { libc::chdir(path.as_ptr()) };
513        result.errno_if_m1().map(drop)
514    }
515}
516
517impl Clock for RealSystem {
518    fn now(&self) -> Instant {
519        Instant::now()
520    }
521}
522
523impl Times for RealSystem {
524    /// Returns consumed CPU times.
525    ///
526    /// This function actually uses `getrusage` rather than `times` because it
527    /// provides better resolution on many systems.
528    fn times(&self) -> Result<CpuTimes> {
529        let mut usage = MaybeUninit::<libc::rusage>::uninit();
530
531        unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) }.errno_if_m1()?;
532        let self_user = unsafe {
533            (*usage.as_ptr()).ru_utime.tv_sec as f64
534                + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
535        };
536        let self_system = unsafe {
537            (*usage.as_ptr()).ru_stime.tv_sec as f64
538                + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
539        };
540
541        unsafe { libc::getrusage(libc::RUSAGE_CHILDREN, usage.as_mut_ptr()) }.errno_if_m1()?;
542        let children_user = unsafe {
543            (*usage.as_ptr()).ru_utime.tv_sec as f64
544                + (*usage.as_ptr()).ru_utime.tv_usec as f64 * 1e-6
545        };
546        let children_system = unsafe {
547            (*usage.as_ptr()).ru_stime.tv_sec as f64
548                + (*usage.as_ptr()).ru_stime.tv_usec as f64 * 1e-6
549        };
550
551        Ok(CpuTimes {
552            self_user,
553            self_system,
554            children_user,
555            children_system,
556        })
557    }
558}
559
560impl GetPid for RealSystem {
561    fn getsid(&self, pid: Pid) -> Result<Pid> {
562        unsafe { libc::getsid(pid.0) }.errno_if_m1().map(Pid)
563    }
564
565    fn getpid(&self) -> Pid {
566        Pid(unsafe { libc::getpid() })
567    }
568
569    fn getppid(&self) -> Pid {
570        Pid(unsafe { libc::getppid() })
571    }
572
573    fn getpgrp(&self) -> Pid {
574        Pid(unsafe { libc::getpgrp() })
575    }
576}
577
578impl SetPgid for RealSystem {
579    fn setpgid(&self, pid: Pid, pgid: Pid) -> Result<()> {
580        let result = unsafe { libc::setpgid(pid.0, pgid.0) };
581        result.errno_if_m1().map(drop)
582    }
583}
584
585const fn to_signal_number(sig: c_int) -> signal::Number {
586    signal::Number::from_raw_unchecked(NonZero::new(sig).unwrap())
587}
588
589impl Signals for RealSystem {
590    const SIGABRT: signal::Number = to_signal_number(libc::SIGABRT);
591    const SIGALRM: signal::Number = to_signal_number(libc::SIGALRM);
592    const SIGBUS: signal::Number = to_signal_number(libc::SIGBUS);
593    const SIGCHLD: signal::Number = to_signal_number(libc::SIGCHLD);
594    #[cfg(any(
595        target_os = "aix",
596        target_os = "horizon",
597        target_os = "illumos",
598        target_os = "solaris",
599    ))]
600    const SIGCLD: Option<signal::Number> = Some(to_signal_number(libc::SIGCLD));
601    #[cfg(not(any(
602        target_os = "aix",
603        target_os = "horizon",
604        target_os = "illumos",
605        target_os = "solaris",
606    )))]
607    const SIGCLD: Option<signal::Number> = None;
608    const SIGCONT: signal::Number = to_signal_number(libc::SIGCONT);
609    #[cfg(not(any(
610        target_os = "android",
611        target_os = "emscripten",
612        target_os = "fuchsia",
613        target_os = "haiku",
614        target_os = "linux",
615        target_os = "redox",
616    )))]
617    const SIGEMT: Option<signal::Number> = Some(to_signal_number(libc::SIGEMT));
618    #[cfg(any(
619        target_os = "android",
620        target_os = "emscripten",
621        target_os = "fuchsia",
622        target_os = "haiku",
623        target_os = "linux",
624        target_os = "redox",
625    ))]
626    const SIGEMT: Option<signal::Number> = None;
627    const SIGFPE: signal::Number = to_signal_number(libc::SIGFPE);
628    const SIGHUP: signal::Number = to_signal_number(libc::SIGHUP);
629    const SIGILL: signal::Number = to_signal_number(libc::SIGILL);
630    #[cfg(not(any(
631        target_os = "aix",
632        target_os = "android",
633        target_os = "emscripten",
634        target_os = "fuchsia",
635        target_os = "haiku",
636        target_os = "linux",
637        target_os = "redox",
638    )))]
639    const SIGINFO: Option<signal::Number> = Some(to_signal_number(libc::SIGINFO));
640    #[cfg(any(
641        target_os = "aix",
642        target_os = "android",
643        target_os = "emscripten",
644        target_os = "fuchsia",
645        target_os = "haiku",
646        target_os = "linux",
647        target_os = "redox",
648    ))]
649    const SIGINFO: Option<signal::Number> = None;
650    const SIGINT: signal::Number = to_signal_number(libc::SIGINT);
651    #[cfg(any(
652        target_os = "aix",
653        target_os = "android",
654        target_os = "emscripten",
655        target_os = "fuchsia",
656        target_os = "horizon",
657        target_os = "illumos",
658        target_os = "linux",
659        target_os = "nto",
660        target_os = "solaris",
661    ))]
662    const SIGIO: Option<signal::Number> = Some(to_signal_number(libc::SIGIO));
663    #[cfg(not(any(
664        target_os = "aix",
665        target_os = "android",
666        target_os = "emscripten",
667        target_os = "fuchsia",
668        target_os = "horizon",
669        target_os = "illumos",
670        target_os = "linux",
671        target_os = "nto",
672        target_os = "solaris",
673    )))]
674    const SIGIO: Option<signal::Number> = None;
675    const SIGIOT: signal::Number = to_signal_number(libc::SIGIOT);
676    const SIGKILL: signal::Number = to_signal_number(libc::SIGKILL);
677    #[cfg(target_os = "horizon")]
678    const SIGLOST: Option<signal::Number> = Some(to_signal_number(libc::SIGLOST));
679    #[cfg(not(target_os = "horizon"))]
680    const SIGLOST: Option<signal::Number> = None;
681    const SIGPIPE: signal::Number = to_signal_number(libc::SIGPIPE);
682    #[cfg(any(
683        target_os = "aix",
684        target_os = "android",
685        target_os = "emscripten",
686        target_os = "fuchsia",
687        target_os = "haiku",
688        target_os = "horizon",
689        target_os = "illumos",
690        target_os = "linux",
691        target_os = "nto",
692        target_os = "solaris",
693    ))]
694    const SIGPOLL: Option<signal::Number> = Some(to_signal_number(libc::SIGPOLL));
695    #[cfg(not(any(
696        target_os = "aix",
697        target_os = "android",
698        target_os = "emscripten",
699        target_os = "fuchsia",
700        target_os = "haiku",
701        target_os = "horizon",
702        target_os = "illumos",
703        target_os = "linux",
704        target_os = "nto",
705        target_os = "solaris",
706    )))]
707    const SIGPOLL: Option<signal::Number> = None;
708    const SIGPROF: signal::Number = to_signal_number(libc::SIGPROF);
709    #[cfg(any(
710        target_os = "aix",
711        target_os = "android",
712        target_os = "emscripten",
713        target_os = "fuchsia",
714        target_os = "illumos",
715        target_os = "linux",
716        target_os = "nto",
717        target_os = "redox",
718        target_os = "solaris",
719    ))]
720    const SIGPWR: Option<signal::Number> = Some(to_signal_number(libc::SIGPWR));
721    #[cfg(not(any(
722        target_os = "aix",
723        target_os = "android",
724        target_os = "emscripten",
725        target_os = "fuchsia",
726        target_os = "illumos",
727        target_os = "linux",
728        target_os = "nto",
729        target_os = "redox",
730        target_os = "solaris",
731    )))]
732    const SIGPWR: Option<signal::Number> = None;
733    const SIGQUIT: signal::Number = to_signal_number(libc::SIGQUIT);
734    const SIGSEGV: signal::Number = to_signal_number(libc::SIGSEGV);
735    #[cfg(all(
736        any(
737            target_os = "android",
738            target_os = "emscripten",
739            target_os = "fuchsia",
740            target_os = "linux"
741        ),
742        not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))
743    ))]
744    const SIGSTKFLT: Option<signal::Number> = Some(to_signal_number(libc::SIGSTKFLT));
745    #[cfg(not(all(
746        any(
747            target_os = "android",
748            target_os = "emscripten",
749            target_os = "fuchsia",
750            target_os = "linux"
751        ),
752        not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))
753    )))]
754    const SIGSTKFLT: Option<signal::Number> = None;
755    const SIGSTOP: signal::Number = to_signal_number(libc::SIGSTOP);
756    const SIGSYS: signal::Number = to_signal_number(libc::SIGSYS);
757    const SIGTERM: signal::Number = to_signal_number(libc::SIGTERM);
758    #[cfg(target_os = "freebsd")]
759    const SIGTHR: Option<signal::Number> = Some(to_signal_number(libc::SIGTHR));
760    #[cfg(not(target_os = "freebsd"))]
761    const SIGTHR: Option<signal::Number> = None;
762    const SIGTRAP: signal::Number = to_signal_number(libc::SIGTRAP);
763    const SIGTSTP: signal::Number = to_signal_number(libc::SIGTSTP);
764    const SIGTTIN: signal::Number = to_signal_number(libc::SIGTTIN);
765    const SIGTTOU: signal::Number = to_signal_number(libc::SIGTTOU);
766    const SIGURG: signal::Number = to_signal_number(libc::SIGURG);
767    const SIGUSR1: signal::Number = to_signal_number(libc::SIGUSR1);
768    const SIGUSR2: signal::Number = to_signal_number(libc::SIGUSR2);
769    const SIGVTALRM: signal::Number = to_signal_number(libc::SIGVTALRM);
770    const SIGWINCH: signal::Number = to_signal_number(libc::SIGWINCH);
771    const SIGXCPU: signal::Number = to_signal_number(libc::SIGXCPU);
772    const SIGXFSZ: signal::Number = to_signal_number(libc::SIGXFSZ);
773
774    fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
775        let raw_range = signal::rt_range();
776        let start = signal::Number::from_raw_unchecked(NonZero::new(*raw_range.start())?);
777        let end = signal::Number::from_raw_unchecked(NonZero::new(*raw_range.end())?);
778        Some(start..=end).filter(|range| !range.is_empty())
779    }
780
781    // TODO: Implement sig2str and str2sig methods
782}
783
784impl Sigmask for RealSystem {
785    type Sigset = Sigset;
786
787    fn sigmask(
788        &self,
789        op: Option<(SigmaskOp, &Sigset)>,
790        old_mask: Option<&mut Sigset>,
791    ) -> impl Future<Output = Result<()>> + use<> {
792        ready(unsafe {
793            let (how, raw_mask) = match op {
794                None => (libc::SIG_BLOCK, std::ptr::null()),
795                Some((op, mask)) => {
796                    let how = match op {
797                        SigmaskOp::Add => libc::SIG_BLOCK,
798                        SigmaskOp::Remove => libc::SIG_UNBLOCK,
799                        SigmaskOp::Set => libc::SIG_SETMASK,
800                    };
801                    (how, mask.0.as_ptr())
802                }
803            };
804            let raw_old_mask =
805                old_mask.map_or(std::ptr::null_mut(), |old_mask| old_mask.0.as_mut_ptr());
806
807            libc::sigprocmask(how, raw_mask, raw_old_mask)
808                .errno_if_m1()
809                .map(drop)
810        })
811    }
812}
813
814impl GetSigaction for RealSystem {
815    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
816        sigaction_impl(signal, None)
817    }
818}
819
820impl Sigaction for RealSystem {
821    fn sigaction(&self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
822        sigaction_impl(signal, Some(handling))
823    }
824}
825
826impl CaughtSignals for RealSystem {
827    fn caught_signals(&self) -> Vec<signal::Number> {
828        let mut signals = Vec::new();
829        for slot in &CAUGHT_SIGNALS {
830            // Need a fence to ensure we examine the slots in order.
831            compiler_fence(Ordering::Acquire);
832
833            let number = slot.swap(0, Ordering::Relaxed);
834            let Some(number) = NonZero::new(number as signal::RawNumber) else {
835                // The `catch_signal` function always fills the first unused
836                // slot, so there is no more slot filled with a signal.
837                break;
838            };
839            signals.push(signal::Number::from_raw_unchecked(number));
840        }
841        signals
842    }
843}
844
845impl SendSignal for RealSystem {
846    fn kill(
847        &self,
848        target: Pid,
849        signal: Option<signal::Number>,
850    ) -> impl Future<Output = Result<()>> + use<> {
851        let raw = signal.map_or(0, signal::Number::as_raw);
852        let result = unsafe { libc::kill(target.0, raw) }.errno_if_m1().map(drop);
853        ready(result)
854    }
855
856    fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<> {
857        let raw = signal.as_raw();
858        let result = unsafe { libc::raise(raw) }.errno_if_m1().map(drop);
859        ready(result)
860    }
861}
862
863impl Select for RealSystem {
864    type FdSet = FdSet;
865
866    fn select<'a>(
867        &self,
868        readers: &'a mut FdSet,
869        writers: &'a mut FdSet,
870        timeout: Option<Duration>,
871        signal_mask: Option<&Sigset>,
872    ) -> impl Future<Output = Result<c_int>> + use<'a> {
873        ready({
874            use std::ptr::{null, null_mut};
875
876            let upper_bound = readers.upper_bound().max(writers.upper_bound());
877            let readers_ptr = readers.as_mut_ptr();
878            let writers_ptr = writers.as_mut_ptr();
879            let errors = null_mut();
880
881            let timeout_spec = timeout.map(to_timespec);
882            let timeout_ptr = timeout_spec.as_ref().map_or(null(), |spec| spec.as_ptr());
883
884            let raw_mask_ptr = signal_mask.map_or(null(), |mask| mask.0.as_ptr());
885
886            unsafe {
887                libc::pselect(
888                    upper_bound.0,
889                    readers_ptr,
890                    writers_ptr,
891                    errors,
892                    timeout_ptr,
893                    raw_mask_ptr,
894                )
895            }
896            .errno_if_m1()
897        })
898    }
899}
900
901impl Isatty for RealSystem {
902    fn isatty(&self, fd: Fd) -> bool {
903        (unsafe { libc::isatty(fd.0) } != 0)
904    }
905}
906
907impl TcGetPgrp for RealSystem {
908    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
909        unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
910    }
911}
912
913impl TcSetPgrp for RealSystem {
914    fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<> {
915        let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
916        ready(result.errno_if_m1().map(drop))
917    }
918}
919
920impl Fork for RealSystem {
921    /// Runs a task in a new child process.
922    ///
923    /// This implementation calls the `fork` system call to create a new
924    /// process. The child task must run to completion without returning
925    /// `Poll::Pending` and terminate the process by calling
926    /// [`RealSystem::exit`] or signaling itself. If a [`Future::poll`] call
927    /// for the child task returns, the child process will panic.
928    fn run_in_child_process<D, F>(&self, shared_data: D, child_task: F) -> (Result<Pid>, D)
929    where
930        D: Clone + 'static,
931        F: AsyncFnOnce(Self, D) + 'static,
932    {
933        match unsafe { libc::fork() }.errno_if_m1() {
934            Ok(0) => {}                                            // Child process
935            Ok(raw_pid) => return (Ok(Pid(raw_pid)), shared_data), // Parent process
936            Err(e) => return (Err(e), shared_data),
937        }
938
939        // Child process
940        let child_task = pin!(child_task(RealSystem(()), shared_data));
941        let mut context = Context::from_waker(Waker::noop());
942        _ = child_task.poll(&mut context);
943        panic!("child task running in RealSystem should not return");
944    }
945
946    /// Creates a new child process.
947    ///
948    /// This implementation calls the `fork` system call and returns both in the
949    /// parent and child process. In the parent, the returned
950    /// `ChildProcessStarter` ignores any arguments and returns the child
951    /// process ID. In the child, the starter runs the task and exits the
952    /// process.
953    fn new_child_process(&self) -> Result<ChildProcessStarter<Self>> {
954        let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
955        if raw_pid != 0 {
956            // Parent process
957            return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
958        }
959
960        // Child process
961        Ok(Box::new(|env, task| {
962            let system = Rc::clone(&env.system);
963            match system.run_real(task(env)) {}
964        }))
965    }
966}
967
968impl Wait for RealSystem {
969    fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
970        let mut status = 0;
971        let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
972        match unsafe { libc::waitpid(target.0, &mut status, options) } {
973            -1 => Err(Errno::last()),
974            0 => Ok(None),
975            pid => {
976                let state = if libc::WIFCONTINUED(status) {
977                    ProcessState::Running
978                } else if libc::WIFEXITED(status) {
979                    let exit_status = libc::WEXITSTATUS(status);
980                    ProcessState::exited(exit_status)
981                } else if libc::WIFSIGNALED(status) {
982                    let signal = libc::WTERMSIG(status);
983                    let core_dump = libc::WCOREDUMP(status);
984                    // SAFETY: The signal number is always a valid signal number, which is non-zero.
985                    let raw_number = unsafe { NonZero::new_unchecked(signal) };
986                    let signal = signal::Number::from_raw_unchecked(raw_number);
987                    let process_result = ProcessResult::Signaled { signal, core_dump };
988                    process_result.into()
989                } else if libc::WIFSTOPPED(status) {
990                    let signal = libc::WSTOPSIG(status);
991                    // SAFETY: The signal number is always a valid signal number, which is non-zero.
992                    let raw_number = unsafe { NonZero::new_unchecked(signal) };
993                    let signal = signal::Number::from_raw_unchecked(raw_number);
994                    ProcessState::stopped(signal)
995                } else {
996                    unreachable!()
997                };
998                Ok(Some((Pid(pid), state)))
999            }
1000        }
1001    }
1002}
1003
1004impl Exec for RealSystem {
1005    fn execve(
1006        &self,
1007        path: &CStr,
1008        args: &[CString],
1009        envs: &[CString],
1010    ) -> impl Future<Output = Result<Infallible>> + use<> {
1011        fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
1012            strs.iter()
1013                .map(|s| s.as_ref().as_ptr())
1014                .chain(std::iter::once(std::ptr::null()))
1015                .collect()
1016        }
1017        // TODO Uncomment when upgrading to libc 1.0
1018        // // This function makes mutable char pointers from immutable string
1019        // // slices since `execve` requires mutable pointers.
1020        // fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*mut libc::c_char> {
1021        //     strs.iter()
1022        //         .map(|s| s.as_ref().as_ptr().cast_mut())
1023        //         .chain(std::iter::once(std::ptr::null_mut()))
1024        //         .collect()
1025        // }
1026
1027        let args = to_pointer_array(args);
1028        let envs = to_pointer_array(envs);
1029        loop {
1030            let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
1031            let errno = Errno::last();
1032            if errno != Errno::EINTR {
1033                return ready(Err(errno));
1034            }
1035        }
1036    }
1037}
1038
1039impl Exit for RealSystem {
1040    fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<> {
1041        (unsafe { libc::_exit(exit_status.0) }) as std::future::Ready<Infallible>
1042    }
1043}
1044
1045impl GetUid for RealSystem {
1046    fn getuid(&self) -> Uid {
1047        Uid(unsafe { libc::getuid() })
1048    }
1049
1050    fn geteuid(&self) -> Uid {
1051        Uid(unsafe { libc::geteuid() })
1052    }
1053
1054    fn getgid(&self) -> Gid {
1055        Gid(unsafe { libc::getgid() })
1056    }
1057
1058    fn getegid(&self) -> Gid {
1059        Gid(unsafe { libc::getegid() })
1060    }
1061}
1062
1063impl GetPw for RealSystem {
1064    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
1065        Errno::clear();
1066        let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
1067        if passwd.is_null() {
1068            let errno = Errno::last();
1069            return if errno == Errno::NO_ERROR {
1070                Ok(None)
1071            } else {
1072                Err(errno)
1073            };
1074        }
1075
1076        let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
1077        Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
1078    }
1079}
1080
1081impl Sysconf for RealSystem {
1082    fn confstr_path(&self) -> Result<UnixString> {
1083        // TODO Support other platforms
1084        #[cfg(any(
1085            target_os = "linux",
1086            target_os = "macos",
1087            target_os = "ios",
1088            target_os = "tvos",
1089            target_os = "watchos"
1090        ))]
1091        unsafe {
1092            let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
1093            if size == 0 {
1094                return Err(Errno::last());
1095            }
1096            let mut buffer = Vec::<u8>::with_capacity(size);
1097            let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
1098            if final_size == 0 {
1099                return Err(Errno::last());
1100            }
1101            if final_size > size {
1102                return Err(Errno::ERANGE);
1103            }
1104            buffer.set_len(final_size - 1); // The last byte is a null terminator.
1105            return Ok(UnixString::from_vec(buffer));
1106        }
1107
1108        #[allow(unreachable_code, reason = "for readability")] // TODO: use cfg_select
1109        Err(Errno::ENOSYS)
1110    }
1111}
1112
1113impl ShellPath for RealSystem {
1114    /// Returns the path to the shell.
1115    ///
1116    /// On Linux, this function returns `/proc/self/exe`. On other platforms, it
1117    /// searches for an executable `sh` from the default PATH returned by
1118    /// [`confstr_path`](Self::confstr_path).
1119    fn shell_path(&self) -> CString {
1120        #[cfg(any(target_os = "linux", target_os = "android"))]
1121        if self.is_executable_file(c"/proc/self/exe") {
1122            return c"/proc/self/exe".to_owned();
1123        }
1124        // TODO Add optimization for other targets
1125
1126        // Find an executable "sh" from the default PATH
1127        if let Ok(path) = self.confstr_path() {
1128            if let Some(full_path) = path
1129                .as_bytes()
1130                .split(|b| *b == b':')
1131                .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
1132                .filter(|full_path| full_path.is_absolute())
1133                .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
1134                .find(|full_path| self.is_executable_file(full_path))
1135            {
1136                return full_path;
1137            }
1138        }
1139
1140        // The last resort
1141        c"/bin/sh".to_owned()
1142    }
1143}
1144
1145impl GetRlimit for RealSystem {
1146    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
1147        let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1148
1149        let mut limits = MaybeUninit::<libc::rlimit>::uninit();
1150        unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
1151
1152        // SAFETY: These two fields of `limits` have been initialized by `getrlimit`.
1153        // (But that does not mean *all* fields are initialized,
1154        // so we cannot use `assume_init` here.)
1155        Ok(LimitPair {
1156            soft: unsafe { (*limits.as_ptr()).rlim_cur },
1157            hard: unsafe { (*limits.as_ptr()).rlim_max },
1158        })
1159    }
1160}
1161
1162impl SetRlimit for RealSystem {
1163    fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
1164        let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
1165
1166        let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
1167        unsafe {
1168            (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
1169            (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
1170        }
1171
1172        unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
1173        Ok(())
1174    }
1175}
1176
1177/// Implementor of [`Dir`] that iterates on a real directory
1178#[derive(Debug)]
1179struct RealDir(NonNull<DIR>);
1180
1181impl Drop for RealDir {
1182    fn drop(&mut self) {
1183        unsafe {
1184            libc::closedir(self.0.as_ptr());
1185        }
1186    }
1187}
1188
1189impl Dir for RealDir {
1190    fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
1191        Errno::clear();
1192        let entry = unsafe { libc::readdir(self.0.as_ptr()) };
1193        let errno = Errno::last();
1194        if entry.is_null() {
1195            if errno == Errno::NO_ERROR {
1196                Ok(None)
1197            } else {
1198                Err(errno)
1199            }
1200        } else {
1201            // TODO Use as_ptr rather than cast when array_ptr_get is stabilized
1202            let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
1203            let name = UnixStr::from_bytes(name.to_bytes());
1204            Ok(Some(DirEntry { name }))
1205        }
1206    }
1207}
1208
1209#[cfg(test)]
1210mod tests {
1211    use super::*;
1212
1213    #[test]
1214    fn real_system_directory_entries() {
1215        let system = unsafe { RealSystem::new() };
1216        let mut dir = system.opendir(c".").unwrap();
1217        let mut count = 0;
1218        while dir.next().unwrap().is_some() {
1219            count += 1;
1220        }
1221        assert!(count > 0);
1222    }
1223
1224    // This test depends on static variables.
1225    #[test]
1226    fn real_system_caught_signals() {
1227        unsafe {
1228            let system = RealSystem::new();
1229            let result = system.caught_signals();
1230            assert_eq!(result, []);
1231
1232            catch_signal(libc::SIGINT);
1233            catch_signal(libc::SIGTERM);
1234            catch_signal(libc::SIGTERM);
1235            catch_signal(libc::SIGCHLD);
1236
1237            let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
1238            let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
1239            let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
1240
1241            let result = system.caught_signals();
1242            assert_eq!(result, [sigint, sigterm, sigchld]);
1243            let result = system.caught_signals();
1244            assert_eq!(result, []);
1245        }
1246    }
1247}