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