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