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::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    /// Convenience function to convert a result of -1 to an `Error` with the
129    /// current `errno`.
130    ///
131    /// This function is intended to be used just after calling a function that
132    /// returns -1 on error and sets `errno` to the error number. This function
133    /// filters out the `-1` result and returns an error containing the current
134    /// `errno`.
135    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/// Converts a `Duration` to a `timespec`.
161///
162/// The return value is a `MaybeUninit` because the `timespec` struct may have
163/// padding or extension fields that are not initialized by this function.
164#[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
175/// Array of slots to store caught signals.
176///
177/// This array is used to store caught signals. All slots are initialized with
178/// 0, which indicates that the slot is available. When a signal is caught, the
179/// signal number is written into one of unoccupied slots.
180static CAUGHT_SIGNALS: [AtomicIsize; 8] = [const { AtomicIsize::new(0) }; 8];
181
182/// Signal catching function.
183///
184/// This function is set as a signal handler for all signals that the shell
185/// wants to catch. When a signal is caught, the signal number is written into
186/// one of the slots in [`CAUGHT_SIGNALS`].
187extern "C" fn catch_signal(signal: c_int) {
188    // This function can only perform async-signal-safe operations.
189    // Performing unsafe operations is undefined behavior!
190
191    // Find an unused slot (having a value of 0) in CAUGHT_SIGNALS and write the
192    // signal number into it.
193    // If there is a slot having a value of the signal already, do nothing.
194    // If there is no available slot, the signal will be lost!
195    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    // SAFETY: We're just creating a new raw pointer to the struct field.
213    // Nothing is dereferenced.
214    let old_mask_ptr = unsafe { &raw mut (*old_action.as_mut_ptr()).sa_mask };
215    // POSIX requires *all* sigset_t objects to be initialized before use,
216    // even if they are output parameters.
217    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    // SAFETY: the `old_action` has been initialized by `sigaction`.
223    let old_disposition = unsafe { Disposition::from_sigaction(&old_action) };
224    Ok(old_disposition)
225}
226
227/// Implementation of system traits that actually interact with the underlying system
228///
229/// `RealSystem` is an empty `struct` because the underlying operating system
230/// manages the system's internal state.
231///
232/// Some trait methods implemented by `RealSystem` (such as [`SendSignal::kill`])
233/// return futures, but the returned futures do not perform asynchronous operations.
234/// Those methods block the current thread until the underlying system calls
235/// complete and then return ready futures.
236/// See also the [`system` module](super) documentation.
237#[derive(Debug)]
238pub struct RealSystem(());
239
240impl RealSystem {
241    /// Returns an instance of `RealSystem`.
242    ///
243    /// # Safety
244    ///
245    /// This function is marked `unsafe` because improper use of `RealSystem`
246    /// may lead to undefined behavior. Remember that most operations performed
247    /// on the system by [`Env`] are not thread-safe. You should never use
248    /// `RealSystem` in a multi-threaded program, and it is your responsibility
249    /// to make sure you are using only one instance of `ReadSystem` in the
250    /// process.
251    pub unsafe fn new() -> Self {
252        RealSystem(())
253    }
254
255    // TODO Should use AT_EACCESS on all platforms
256    #[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        // TODO Use as_mut_ptr rather than cast when array_ptr_get is stabilized
304        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            // Rust does not perform default argument promotion for C variadic
347            // functions, so we need to promote manually.
348            #[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        // Clear the CLOEXEC flag
365        _ = 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        // TODO: Use posix_close when available
386        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    /// Reads data from the file descriptor into the buffer.
439    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    /// Writes data from the buffer to the file descriptor.
452    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        // Some getcwd implementations allocate a buffer for the path if the
483        // first argument is null, but we cannot use that feature because Vec's
484        // allocator may not be compatible with the system's allocator.
485
486        // Since there is no way to know the required buffer size, we try
487        // several buffer sizes.
488        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                // len does not include the null terminator
495                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    /// Returns consumed CPU times.
524    ///
525    /// This function actually uses `getrusage` rather than `times` because it
526    /// provides better resolution on many systems.
527    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    // TODO: Implement sig2str and str2sig methods
781}
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            // Need a fence to ensure we examine the slots in order.
830            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                // The `catch_signal` function always fills the first unused
835                // slot, so there is no more slot filled with a signal.
836                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    /// Runs a task in a new child process.
921    ///
922    /// This implementation calls the `fork` system call to create a new
923    /// process. The child task must run to completion without returning
924    /// `Poll::Pending` and terminate the process by calling
925    /// [`RealSystem::exit`] or signaling itself. If a [`Future::poll`] call
926    /// for the child task returns, the child process will panic.
927    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) => {}                                            // Child process
934            Ok(raw_pid) => return (Ok(Pid(raw_pid)), shared_data), // Parent process
935            Err(e) => return (Err(e), shared_data),
936        }
937
938        // Child process
939        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                    // SAFETY: The signal number is always a valid signal number, which is non-zero.
963                    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                    // SAFETY: The signal number is always a valid signal number, which is non-zero.
970                    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            // SAFETY: `AsCStrArray::as_ptr` returns a pointer to a
997            // null-terminated array of pointers to valid C-style strings, which
998            // is exactly what `execve` expects. The strings remain valid because
999            // `args` and `envs` are kept alive for the duration of the call.
1000            let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
1001            // TODO Uncomment when upgrading to libc 1.0
1002            // // `execve` requires mutable pointers in libc 1.0.
1003            // let _ = unsafe {
1004            //     libc::execve(path.as_ptr(), args.as_mut_ptr(), envs.as_mut_ptr())
1005            // };
1006            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        // TODO Support other platforms
1059        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); // The last byte is a null terminator.
1082                    Ok(UnixString::from_vec(buffer))
1083                }
1084            }
1085
1086            _ => Err(Errno::ENOSYS),
1087        }
1088    }
1089}
1090
1091impl ShellPath for RealSystem {
1092    /// Returns the path to the shell.
1093    ///
1094    /// On Linux, this function returns `/proc/self/exe`. On other platforms, it
1095    /// searches for an executable `sh` from the default PATH returned by
1096    /// [`confstr_path`](Self::confstr_path).
1097    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        // TODO Add optimization for other targets
1103
1104        // Find an executable "sh" from the default PATH
1105        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        // The last resort
1118        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        // SAFETY: These two fields of `limits` have been initialized by `getrlimit`.
1130        // (But that does not mean *all* fields are initialized,
1131        // so we cannot use `assume_init` here.)
1132        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/// Implementor of [`Dir`] that iterates on a real directory
1155#[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            // TODO Use as_ptr rather than cast when array_ptr_get is stabilized
1179            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    // This test depends on static variables.
1202    #[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}