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    fn times(&self) -> Result<Times> {
423        let mut tms = MaybeUninit::<libc::tms>::uninit();
424        let raw_result = unsafe { libc::times(tms.as_mut_ptr()) };
425        if raw_result == (-1) as _ {
426            return Err(Errno::last());
427        }
428
429        let ticks_per_second = unsafe { libc::sysconf(libc::_SC_CLK_TCK) };
430        if ticks_per_second <= 0 {
431            return Err(Errno::last());
432        }
433
434        // SAFETY: These four fields of `tms` have been initialized by `times`.
435        // (But that does not mean *all* fields are initialized,
436        // so we cannot use `assume_init` here.)
437        let utime = unsafe { (*tms.as_ptr()).tms_utime };
438        let stime = unsafe { (*tms.as_ptr()).tms_stime };
439        let cutime = unsafe { (*tms.as_ptr()).tms_cutime };
440        let cstime = unsafe { (*tms.as_ptr()).tms_cstime };
441
442        Ok(Times {
443            self_user: utime as f64 / ticks_per_second as f64,
444            self_system: stime as f64 / ticks_per_second as f64,
445            children_user: cutime as f64 / ticks_per_second as f64,
446            children_system: cstime as f64 / ticks_per_second as f64,
447        })
448    }
449
450    fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
451        let non_zero = NonZero::new(number)?;
452        let name = signal::Name::try_from_raw_real(number)?;
453        Some((name, signal::Number::from_raw_unchecked(non_zero)))
454    }
455
456    #[inline(always)]
457    fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
458        name.to_raw_real()
459    }
460
461    fn sigmask(
462        &mut self,
463        op: Option<(SigmaskOp, &[signal::Number])>,
464        old_mask: Option<&mut Vec<signal::Number>>,
465    ) -> Result<()> {
466        unsafe {
467            let (how, raw_mask) = match op {
468                None => (libc::SIG_BLOCK, None),
469                Some((op, mask)) => {
470                    let how = match op {
471                        SigmaskOp::Add => libc::SIG_BLOCK,
472                        SigmaskOp::Remove => libc::SIG_UNBLOCK,
473                        SigmaskOp::Set => libc::SIG_SETMASK,
474                    };
475
476                    let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
477                    libc::sigemptyset(raw_mask.as_mut_ptr()).errno_if_m1()?;
478                    for &signal in mask {
479                        libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()).errno_if_m1()?;
480                    }
481
482                    (how, Some(raw_mask))
483                }
484            };
485            let mut old_mask_pair = match old_mask {
486                None => None,
487                Some(old_mask) => {
488                    let mut raw_old_mask = MaybeUninit::<libc::sigset_t>::uninit();
489                    // POSIX requires *all* sigset_t objects to be initialized before use.
490                    libc::sigemptyset(raw_old_mask.as_mut_ptr()).errno_if_m1()?;
491                    Some((old_mask, raw_old_mask))
492                }
493            };
494
495            let raw_set_ptr = raw_mask
496                .as_ref()
497                .map_or(std::ptr::null(), |raw_set| raw_set.as_ptr());
498            let raw_old_set_ptr = old_mask_pair
499                .as_mut()
500                .map_or(std::ptr::null_mut(), |(_, raw_old_mask)| {
501                    raw_old_mask.as_mut_ptr()
502                });
503            let result = libc::sigprocmask(how, raw_set_ptr, raw_old_set_ptr);
504            result.errno_if_m1().map(drop)?;
505
506            if let Some((old_mask, raw_old_mask)) = old_mask_pair {
507                old_mask.clear();
508                signal::sigset_to_vec(raw_old_mask.as_ptr(), old_mask);
509            }
510
511            Ok(())
512        }
513    }
514
515    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition> {
516        sigaction_impl(signal, None)
517    }
518
519    fn sigaction(&mut self, signal: signal::Number, handling: Disposition) -> Result<Disposition> {
520        sigaction_impl(signal, Some(handling))
521    }
522
523    fn caught_signals(&mut self) -> Vec<signal::Number> {
524        let mut signals = Vec::new();
525        for slot in &CAUGHT_SIGNALS {
526            // Need a fence to ensure we examine the slots in order.
527            compiler_fence(Ordering::Acquire);
528
529            let signal = slot.swap(0, Ordering::Relaxed);
530            if signal == 0 {
531                // The `catch_signal` function always fills the first unused
532                // slot, so there is no more slot filled with a signal.
533                break;
534            }
535
536            if let Some((_name, number)) = self.validate_signal(signal as signal::RawNumber) {
537                signals.push(number)
538            } else {
539                // ignore unknown signal
540            }
541        }
542        signals
543    }
544
545    fn kill(&mut self, target: Pid, signal: Option<signal::Number>) -> FlexFuture<Result<()>> {
546        let raw = signal.map_or(0, signal::Number::as_raw);
547        let result = unsafe { libc::kill(target.0, raw) }.errno_if_m1().map(drop);
548        result.into()
549    }
550
551    fn raise(&mut self, signal: signal::Number) -> FlexFuture<Result<()>> {
552        let raw = signal.as_raw();
553        unsafe { libc::raise(raw) }.errno_if_m1().map(drop).into()
554    }
555
556    fn select(
557        &mut self,
558        readers: &mut Vec<Fd>,
559        writers: &mut Vec<Fd>,
560        timeout: Option<Duration>,
561        signal_mask: Option<&[signal::Number]>,
562    ) -> Result<c_int> {
563        use std::ptr::{null, null_mut};
564
565        let max_fd = readers.iter().chain(writers.iter()).max();
566        let nfds = max_fd
567            .map(|fd| fd.0.checked_add(1).ok_or(Errno::EBADF))
568            .transpose()?
569            .unwrap_or(0);
570
571        fn to_raw_fd_set(fds: &[Fd]) -> MaybeUninit<libc::fd_set> {
572            let mut raw_fds = MaybeUninit::<libc::fd_set>::uninit();
573            unsafe {
574                libc::FD_ZERO(raw_fds.as_mut_ptr());
575                for fd in fds {
576                    libc::FD_SET(fd.0, raw_fds.as_mut_ptr());
577                }
578            }
579            raw_fds
580        }
581        let mut raw_readers = to_raw_fd_set(readers);
582        let mut raw_writers = to_raw_fd_set(writers);
583        let readers_ptr = raw_readers.as_mut_ptr();
584        let writers_ptr = raw_writers.as_mut_ptr();
585        let errors = null_mut();
586
587        let timeout_spec = to_timespec(timeout.unwrap_or_default());
588        let timeout_ptr = if timeout.is_some() {
589            timeout_spec.as_ptr()
590        } else {
591            null()
592        };
593
594        let mut raw_mask = MaybeUninit::<libc::sigset_t>::uninit();
595        let raw_mask_ptr = match signal_mask {
596            None => null(),
597            Some(signal_mask) => {
598                unsafe { libc::sigemptyset(raw_mask.as_mut_ptr()) }.errno_if_m1()?;
599                for &signal in signal_mask {
600                    unsafe { libc::sigaddset(raw_mask.as_mut_ptr(), signal.as_raw()) }
601                        .errno_if_m1()?;
602                }
603                raw_mask.as_ptr()
604            }
605        };
606
607        let count = unsafe {
608            libc::pselect(
609                nfds,
610                readers_ptr,
611                writers_ptr,
612                errors,
613                timeout_ptr,
614                raw_mask_ptr,
615            )
616        }
617        .errno_if_m1()?;
618
619        readers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, readers_ptr) });
620        writers.retain(|fd| unsafe { libc::FD_ISSET(fd.0, writers_ptr) });
621
622        Ok(count)
623    }
624
625    fn getsid(&self, pid: Pid) -> Result<Pid> {
626        unsafe { libc::getsid(pid.0) }.errno_if_m1().map(Pid)
627    }
628
629    fn getpid(&self) -> Pid {
630        Pid(unsafe { libc::getpid() })
631    }
632
633    fn getppid(&self) -> Pid {
634        Pid(unsafe { libc::getppid() })
635    }
636
637    fn getpgrp(&self) -> Pid {
638        Pid(unsafe { libc::getpgrp() })
639    }
640
641    fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()> {
642        let result = unsafe { libc::setpgid(pid.0, pgid.0) };
643        result.errno_if_m1().map(drop)
644    }
645
646    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
647        unsafe { libc::tcgetpgrp(fd.0) }.errno_if_m1().map(Pid)
648    }
649
650    fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
651        let result = unsafe { libc::tcsetpgrp(fd.0, pgid.0) };
652        result.errno_if_m1().map(drop)
653    }
654
655    /// Creates a new child process.
656    ///
657    /// This implementation calls the `fork` system call and returns both in the
658    /// parent and child process. In the parent, the returned
659    /// `ChildProcessStarter` ignores any arguments and returns the child
660    /// process ID. In the child, the starter runs the task and exits the
661    /// process.
662    fn new_child_process(&mut self) -> Result<ChildProcessStarter> {
663        let raw_pid = unsafe { libc::fork() }.errno_if_m1()?;
664        if raw_pid != 0 {
665            // Parent process
666            return Ok(Box::new(move |_env, _task| Pid(raw_pid)));
667        }
668
669        // Child process
670        Ok(Box::new(|env, task| {
671            let system = env.system.clone();
672            // Here we create a new executor to run the task. This makes sure that any
673            // other concurrent tasks in the parent process do not interfere with the
674            // child process.
675            let executor = Executor::new();
676            let task = Box::pin(async move { match task(env).await {} });
677            // SAFETY: We never create new threads in the whole process, so wakers are
678            // never shared between threads.
679            unsafe { executor.spawn_pinned(task) }
680            loop {
681                executor.run_until_stalled();
682                system.select(false).ok();
683            }
684        }))
685    }
686
687    fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
688        let mut status = 0;
689        let options = libc::WUNTRACED | libc::WCONTINUED | libc::WNOHANG;
690        match unsafe { libc::waitpid(target.0, &mut status, options) } {
691            -1 => Err(Errno::last()),
692            0 => Ok(None),
693            pid => {
694                let state = if libc::WIFCONTINUED(status) {
695                    ProcessState::Running
696                } else if libc::WIFEXITED(status) {
697                    let exit_status = libc::WEXITSTATUS(status);
698                    ProcessState::exited(exit_status)
699                } else if libc::WIFSIGNALED(status) {
700                    let signal = libc::WTERMSIG(status);
701                    let core_dump = libc::WCOREDUMP(status);
702                    // SAFETY: The signal number is always a valid signal number, which is non-zero.
703                    let raw_number = unsafe { NonZero::new_unchecked(signal) };
704                    let signal = signal::Number::from_raw_unchecked(raw_number);
705                    let process_result = ProcessResult::Signaled { signal, core_dump };
706                    process_result.into()
707                } else if libc::WIFSTOPPED(status) {
708                    let signal = libc::WSTOPSIG(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                    ProcessState::stopped(signal)
713                } else {
714                    unreachable!()
715                };
716                Ok(Some((Pid(pid), state)))
717            }
718        }
719    }
720
721    fn execve(
722        &mut self,
723        path: &CStr,
724        args: &[CString],
725        envs: &[CString],
726    ) -> FlexFuture<Result<Infallible>> {
727        fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*const libc::c_char> {
728            strs.iter()
729                .map(|s| s.as_ref().as_ptr())
730                .chain(std::iter::once(std::ptr::null()))
731                .collect()
732        }
733        // TODO Uncomment when upgrading to libc 1.0
734        // // This function makes mutable char pointers from immutable string
735        // // slices since `execve` requires mutable pointers.
736        // fn to_pointer_array<S: AsRef<CStr>>(strs: &[S]) -> Vec<*mut libc::c_char> {
737        //     strs.iter()
738        //         .map(|s| s.as_ref().as_ptr().cast_mut())
739        //         .chain(std::iter::once(std::ptr::null_mut()))
740        //         .collect()
741        // }
742
743        let args = to_pointer_array(args);
744        let envs = to_pointer_array(envs);
745        loop {
746            let _ = unsafe { libc::execve(path.as_ptr(), args.as_ptr(), envs.as_ptr()) };
747            let errno = Errno::last();
748            if errno != Errno::EINTR {
749                return Err(errno).into();
750            }
751        }
752    }
753
754    fn exit(&mut self, exit_status: ExitStatus) -> FlexFuture<Infallible> {
755        unsafe { libc::_exit(exit_status.0) }
756    }
757
758    fn getcwd(&self) -> Result<PathBuf> {
759        // Some getcwd implementations allocate a buffer for the path if the
760        // first argument is null, but we cannot use that feature because Vec's
761        // allocator may not be compatible with the system's allocator.
762
763        // Since there is no way to know the required buffer size, we try
764        // several buffer sizes.
765        let mut buffer = Vec::<u8>::new();
766        for capacity in [1 << 10, 1 << 12, 1 << 14, 1 << 16] {
767            buffer.reserve_exact(capacity);
768
769            let result = unsafe { libc::getcwd(buffer.as_mut_ptr().cast(), capacity) };
770            if !result.is_null() {
771                // len does not include the null terminator
772                let len = unsafe { CStr::from_ptr(buffer.as_ptr().cast()) }.count_bytes();
773                unsafe { buffer.set_len(len) }
774                buffer.shrink_to_fit();
775                return Ok(PathBuf::from(UnixString::from_vec(buffer)));
776            }
777            let errno = Errno::last();
778            if errno != Errno::ERANGE {
779                return Err(errno);
780            }
781        }
782        Err(Errno::ERANGE)
783    }
784
785    fn chdir(&mut self, path: &CStr) -> Result<()> {
786        let result = unsafe { libc::chdir(path.as_ptr()) };
787        result.errno_if_m1().map(drop)
788    }
789
790    fn getuid(&self) -> Uid {
791        Uid(unsafe { libc::getuid() })
792    }
793
794    fn geteuid(&self) -> Uid {
795        Uid(unsafe { libc::geteuid() })
796    }
797
798    fn getgid(&self) -> Gid {
799        Gid(unsafe { libc::getgid() })
800    }
801
802    fn getegid(&self) -> Gid {
803        Gid(unsafe { libc::getegid() })
804    }
805
806    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
807        Errno::clear();
808        let passwd = unsafe { libc::getpwnam(name.as_ptr()) };
809        if passwd.is_null() {
810            let errno = Errno::last();
811            return if errno == Errno::NO_ERROR {
812                Ok(None)
813            } else {
814                Err(errno)
815            };
816        }
817
818        let dir = unsafe { CStr::from_ptr((*passwd).pw_dir) };
819        Ok(Some(UnixString::from_vec(dir.to_bytes().to_vec()).into()))
820    }
821
822    fn confstr_path(&self) -> Result<UnixString> {
823        // TODO Support other platforms
824        #[cfg(any(
825            target_os = "linux",
826            target_os = "macos",
827            target_os = "ios",
828            target_os = "tvos",
829            target_os = "watchos"
830        ))]
831        unsafe {
832            let size = libc::confstr(libc::_CS_PATH, std::ptr::null_mut(), 0);
833            if size == 0 {
834                return Err(Errno::last());
835            }
836            let mut buffer = Vec::<u8>::with_capacity(size);
837            let final_size = libc::confstr(libc::_CS_PATH, buffer.as_mut_ptr() as *mut _, size);
838            if final_size == 0 {
839                return Err(Errno::last());
840            }
841            if final_size > size {
842                return Err(Errno::ERANGE);
843            }
844            buffer.set_len(final_size - 1); // The last byte is a null terminator.
845            return Ok(UnixString::from_vec(buffer));
846        }
847
848        #[allow(unreachable_code)]
849        Err(Errno::ENOSYS)
850    }
851
852    /// Returns the path to the shell.
853    ///
854    /// On Linux, this function returns `/proc/self/exe`. On other platforms, it
855    /// searches for an executable `sh` from the default PATH returned by
856    /// [`confstr_path`](Self::confstr_path).
857    fn shell_path(&self) -> CString {
858        #[cfg(any(target_os = "linux", target_os = "android"))]
859        if self.is_executable_file(c"/proc/self/exe") {
860            return c"/proc/self/exe".to_owned();
861        }
862        // TODO Add optimization for other targets
863
864        // Find an executable "sh" from the default PATH
865        if let Ok(path) = self.confstr_path() {
866            if let Some(full_path) = path
867                .as_bytes()
868                .split(|b| *b == b':')
869                .map(|dir| Path::new(UnixStr::from_bytes(dir)).join("sh"))
870                .filter(|full_path| full_path.is_absolute())
871                .filter_map(|full_path| CString::new(full_path.into_unix_string().into_vec()).ok())
872                .find(|full_path| self.is_executable_file(full_path))
873            {
874                return full_path;
875            }
876        }
877
878        // The last resort
879        c"/bin/sh".to_owned()
880    }
881
882    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
883        let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
884
885        let mut limits = MaybeUninit::<libc::rlimit>::uninit();
886        unsafe { libc::getrlimit(raw_resource as _, limits.as_mut_ptr()) }.errno_if_m1()?;
887
888        // SAFETY: These two fields of `limits` have been initialized by `getrlimit`.
889        // (But that does not mean *all* fields are initialized,
890        // so we cannot use `assume_init` here.)
891        Ok(LimitPair {
892            soft: unsafe { (*limits.as_ptr()).rlim_cur },
893            hard: unsafe { (*limits.as_ptr()).rlim_max },
894        })
895    }
896
897    fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()> {
898        let raw_resource = resource.as_raw_type().ok_or(Errno::EINVAL)?;
899
900        let mut rlimit = MaybeUninit::<libc::rlimit>::uninit();
901        unsafe {
902            (&raw mut (*rlimit.as_mut_ptr()).rlim_cur).write(limits.soft);
903            (&raw mut (*rlimit.as_mut_ptr()).rlim_max).write(limits.hard);
904        }
905
906        unsafe { libc::setrlimit(raw_resource as _, rlimit.as_ptr()) }.errno_if_m1()?;
907        Ok(())
908    }
909}
910
911/// Implementor of [`Dir`] that iterates on a real directory
912#[derive(Debug)]
913struct RealDir(NonNull<DIR>);
914
915impl Drop for RealDir {
916    fn drop(&mut self) {
917        unsafe {
918            libc::closedir(self.0.as_ptr());
919        }
920    }
921}
922
923impl Dir for RealDir {
924    fn next(&mut self) -> Result<Option<DirEntry<'_>>> {
925        Errno::clear();
926        let entry = unsafe { libc::readdir(self.0.as_ptr()) };
927        let errno = Errno::last();
928        if entry.is_null() {
929            if errno == Errno::NO_ERROR {
930                Ok(None)
931            } else {
932                Err(errno)
933            }
934        } else {
935            // TODO Use as_ptr rather than cast when array_ptr_get is stabilized
936            let name = unsafe { CStr::from_ptr((&raw const (*entry).d_name).cast()) };
937            let name = UnixStr::from_bytes(name.to_bytes());
938            Ok(Some(DirEntry { name }))
939        }
940    }
941}
942
943#[cfg(test)]
944mod tests {
945    use super::*;
946
947    #[test]
948    fn real_system_directory_entries() {
949        let mut system = unsafe { RealSystem::new() };
950        let mut dir = system.opendir(c".").unwrap();
951        let mut count = 0;
952        while dir.next().unwrap().is_some() {
953            count += 1;
954        }
955        assert!(count > 0);
956    }
957
958    // This test depends on static variables.
959    #[test]
960    fn real_system_caught_signals() {
961        unsafe {
962            let mut system = RealSystem::new();
963            let result = system.caught_signals();
964            assert_eq!(result, []);
965
966            catch_signal(libc::SIGINT);
967            catch_signal(libc::SIGTERM);
968            catch_signal(libc::SIGTERM);
969            catch_signal(libc::SIGCHLD);
970
971            let sigint = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGINT).unwrap());
972            let sigterm = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGTERM).unwrap());
973            let sigchld = signal::Number::from_raw_unchecked(NonZero::new(libc::SIGCHLD).unwrap());
974
975            let result = system.caught_signals();
976            assert_eq!(result, [sigint, sigterm, sigchld]);
977            let result = system.caught_signals();
978            assert_eq!(result, []);
979        }
980    }
981}