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