yash_env/
system.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//! [System] and its implementors.
18
19mod errno;
20mod fd_flag;
21mod file_system;
22mod future;
23mod id;
24mod open_flag;
25#[cfg(unix)]
26pub mod real;
27pub mod resource;
28mod select;
29mod shared;
30pub mod r#virtual;
31
32pub use self::errno::Errno;
33pub use self::errno::RawErrno;
34pub use self::errno::Result;
35pub use self::fd_flag::FdFlag;
36pub use self::file_system::AT_FDCWD;
37pub use self::file_system::Dir;
38pub use self::file_system::DirEntry;
39pub use self::file_system::FileType;
40pub use self::file_system::Mode;
41pub use self::file_system::RawMode;
42pub use self::file_system::Stat;
43pub use self::future::FlexFuture;
44pub use self::id::Gid;
45pub use self::id::RawGid;
46pub use self::id::RawUid;
47pub use self::id::Uid;
48pub use self::open_flag::OfdAccess;
49pub use self::open_flag::OpenFlag;
50#[cfg(all(doc, unix))]
51use self::real::RealSystem;
52use self::resource::LimitPair;
53use self::resource::Resource;
54use self::select::SelectSystem;
55use self::select::SignalStatus;
56pub use self::shared::SharedSystem;
57#[cfg(doc)]
58use self::r#virtual::VirtualSystem;
59use crate::Env;
60use crate::io::Fd;
61use crate::io::MIN_INTERNAL_FD;
62use crate::job::Pid;
63use crate::job::ProcessState;
64use crate::path::Path;
65use crate::path::PathBuf;
66use crate::semantics::ExitStatus;
67use crate::signal;
68use crate::str::UnixString;
69#[cfg(doc)]
70use crate::subshell::Subshell;
71use crate::trap::SignalSystem;
72use enumset::EnumSet;
73use std::convert::Infallible;
74use std::ffi::CStr;
75use std::ffi::CString;
76use std::ffi::c_int;
77use std::fmt::Debug;
78use std::io::SeekFrom;
79use std::pin::Pin;
80use std::time::Duration;
81use std::time::Instant;
82use r#virtual::SignalEffect;
83
84/// API to the system-managed parts of the environment.
85///
86/// The `System` trait defines a collection of methods to access the underlying
87/// operating system from the shell as an application program. There are two
88/// substantial implementors for this trait: [`RealSystem`] and
89/// [`VirtualSystem`]. Another implementor is [`SharedSystem`], which wraps a
90/// `System` instance to extend the interface with asynchronous methods.
91pub trait System: Debug {
92    /// Retrieves metadata of a file.
93    fn fstat(&self, fd: Fd) -> Result<Stat>;
94
95    /// Retrieves metadata of a file.
96    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Stat>;
97
98    /// Whether there is an executable file at the specified path.
99    #[must_use]
100    fn is_executable_file(&self, path: &CStr) -> bool;
101
102    /// Whether there is a directory at the specified path.
103    #[must_use]
104    fn is_directory(&self, path: &CStr) -> bool;
105
106    /// Creates an unnamed pipe.
107    ///
108    /// This is a thin wrapper around the `pipe` system call.
109    /// If successful, returns the reading and writing ends of the pipe.
110    fn pipe(&mut self) -> Result<(Fd, Fd)>;
111
112    /// Duplicates a file descriptor.
113    ///
114    /// This is a thin wrapper around the `fcntl` system call that opens a new
115    /// FD that shares the open file description with `from`. The new FD will be
116    /// the minimum unused FD not less than `to_min`. The `flags` are set to the
117    /// new FD.
118    ///
119    /// If successful, returns `Ok(new_fd)`. On error, returns `Err(_)`.
120    fn dup(&mut self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd>;
121
122    /// Duplicates a file descriptor.
123    ///
124    /// This is a thin wrapper around the `dup2` system call. If successful,
125    /// returns `Ok(to)`. On error, returns `Err(_)`.
126    fn dup2(&mut self, from: Fd, to: Fd) -> Result<Fd>;
127
128    /// Opens a file descriptor.
129    ///
130    /// This is a thin wrapper around the `open` system call.
131    fn open(
132        &mut self,
133        path: &CStr,
134        access: OfdAccess,
135        flags: EnumSet<OpenFlag>,
136        mode: Mode,
137    ) -> Result<Fd>;
138
139    /// Opens a file descriptor associated with an anonymous temporary file.
140    ///
141    /// This function works similarly to the `O_TMPFILE` flag specified to the
142    /// `open` function.
143    fn open_tmpfile(&mut self, parent_dir: &Path) -> Result<Fd>;
144
145    /// Closes a file descriptor.
146    ///
147    /// This is a thin wrapper around the `close` system call.
148    ///
149    /// This function returns `Ok(())` when the FD is already closed.
150    fn close(&mut self, fd: Fd) -> Result<()>;
151
152    /// Returns the open file description access mode.
153    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess>;
154
155    /// Gets and sets the non-blocking mode for the open file description.
156    ///
157    /// This is a wrapper around the `fcntl` system call.
158    /// This function sets the non-blocking mode to the given value and returns
159    /// the previous mode.
160    fn get_and_set_nonblocking(&mut self, fd: Fd, nonblocking: bool) -> Result<bool>;
161
162    /// Returns the attributes for the file descriptor.
163    ///
164    /// This is a thin wrapper around the `fcntl` system call.
165    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>>;
166
167    /// Sets attributes for the file descriptor.
168    ///
169    /// This is a thin wrapper around the `fcntl` system call.
170    fn fcntl_setfd(&mut self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()>;
171
172    /// Tests if a file descriptor is associated with a terminal device.
173    ///
174    /// On error, this function simply returns `false` and no detailed error
175    /// information is provided because POSIX does not require the `isatty`
176    /// function to set `errno`.
177    fn isatty(&self, fd: Fd) -> bool;
178
179    /// Reads from the file descriptor.
180    ///
181    /// This is a thin wrapper around the `read` system call.
182    /// If successful, returns the number of bytes read.
183    ///
184    /// This function may perform blocking I/O, especially if the `O_NONBLOCK`
185    /// flag is not set for the FD. Use [`SharedSystem::read_async`] to support
186    /// concurrent I/O in an `async` function context.
187    fn read(&mut self, fd: Fd, buffer: &mut [u8]) -> Result<usize>;
188
189    /// Writes to the file descriptor.
190    ///
191    /// This is a thin wrapper around the `write` system call.
192    /// If successful, returns the number of bytes written.
193    ///
194    /// This function may write only part of the `buffer` and block if the
195    /// `O_NONBLOCK` flag is not set for the FD. Use [`SharedSystem::write_all`]
196    /// to support concurrent I/O in an `async` function context and ensure the
197    /// whole `buffer` is written.
198    fn write(&mut self, fd: Fd, buffer: &[u8]) -> Result<usize>;
199
200    /// Moves the position of the open file description.
201    fn lseek(&mut self, fd: Fd, position: SeekFrom) -> Result<u64>;
202
203    /// Opens a directory for enumerating entries.
204    fn fdopendir(&mut self, fd: Fd) -> Result<Box<dyn Dir>>;
205
206    /// Opens a directory for enumerating entries.
207    fn opendir(&mut self, path: &CStr) -> Result<Box<dyn Dir>>;
208
209    /// Gets and sets the file creation mode mask.
210    ///
211    /// This is a thin wrapper around the `umask` system call. It sets the mask
212    /// to the given value and returns the previous mask.
213    ///
214    /// You cannot tell the current mask without setting a new one. If you only
215    /// want to get the current mask, you need to set it back to the original
216    /// value after getting it.
217    fn umask(&mut self, new_mask: Mode) -> Mode;
218
219    /// Returns the current time.
220    #[must_use]
221    fn now(&self) -> Instant;
222
223    /// Returns consumed CPU times.
224    fn times(&self) -> Result<Times>;
225
226    /// Tests if a signal number is valid.
227    ///
228    /// This function returns `Some((name, number))` if the signal number refers
229    /// to a valid signal supported by the system. Otherwise, it returns `None`.
230    ///
231    /// Note that one signal number can have multiple names, in which case this
232    /// function returns the name that is considered the most common.
233    #[must_use]
234    fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)>;
235
236    /// Gets the signal number from the signal name.
237    ///
238    /// This function returns the signal number corresponding to the signal name
239    /// in the system. If the signal name is not supported, it returns `None`.
240    #[must_use]
241    fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number>;
242
243    /// Gets and/or sets the signal blocking mask.
244    ///
245    /// This is a low-level function used internally by
246    /// [`SharedSystem::set_disposition`]. You should not call this function
247    /// directly, or you will disrupt the behavior of `SharedSystem`. The
248    /// description below applies if you want to do everything yourself without
249    /// depending on `SharedSystem`.
250    ///
251    /// This is a thin wrapper around the `sigprocmask` system call. If `op` is
252    /// `Some`, this function updates the signal blocking mask by applying the
253    /// given `SigmaskOp` and signal set to the current mask. If `op` is `None`,
254    /// this function does not change the mask.
255    /// If `old_mask` is `Some`, this function sets the previous mask to it.
256    fn sigmask(
257        &mut self,
258        op: Option<(SigmaskOp, &[signal::Number])>,
259        old_mask: Option<&mut Vec<signal::Number>>,
260    ) -> Result<()>;
261
262    /// Gets the disposition for a signal.
263    ///
264    /// This is a low-level function used internally by
265    /// [`SharedSystem::get_disposition`]. You should not call this function
266    /// directly, or you will leave the `SharedSystem` instance in an
267    /// inconsistent state. The description below applies if you want to do
268    /// everything yourself without depending on `SharedSystem`.
269    ///
270    /// This is an abstract wrapper around the `sigaction` system call. This
271    /// function returns the current disposition if successful.
272    ///
273    /// To change the disposition, use [`sigaction`](Self::sigaction).
274    fn get_sigaction(&self, signal: signal::Number) -> Result<Disposition>;
275
276    /// Gets and sets the disposition for a signal.
277    ///
278    /// This is a low-level function used internally by
279    /// [`SharedSystem::set_disposition`]. You should not call this function
280    /// directly, or you will leave the `SharedSystem` instance in an
281    /// inconsistent state. The description below applies if you want to do
282    /// everything yourself without depending on `SharedSystem`.
283    ///
284    /// This is an abstract wrapper around the `sigaction` system call. This
285    /// function returns the previous disposition if successful.
286    ///
287    /// When you set the disposition to `Disposition::Catch`, signals sent to
288    /// this process are accumulated in the `System` instance and made available
289    /// from [`caught_signals`](Self::caught_signals).
290    ///
291    /// To get the current disposition without changing it, use
292    /// [`get_sigaction`](Self::get_sigaction).
293    fn sigaction(&mut self, signal: signal::Number, action: Disposition) -> Result<Disposition>;
294
295    /// Returns signals this process has caught, if any.
296    ///
297    /// This is a low-level function used internally by
298    /// [`SharedSystem::select`]. You should not call this function directly, or
299    /// you will disrupt the behavior of `SharedSystem`. The description below
300    /// applies if you want to do everything yourself without depending on
301    /// `SharedSystem`.
302    ///
303    /// To catch a signal, you must firstly install a signal handler by calling
304    /// [`sigaction`](Self::sigaction) with [`Disposition::Catch`]. Once the
305    /// handler is ready, signals sent to the process are accumulated in the
306    /// `System`. You call `caught_signals` to obtain a list of caught signals
307    /// thus far.
308    ///
309    /// This function clears the internal list of caught signals, so a next call
310    /// will return an empty list unless another signal is caught since the
311    /// first call. Because the list size is limited, you should call this
312    /// function periodically before the list gets full, in which case further
313    /// caught signals are silently ignored.
314    ///
315    /// Note that signals become pending if sent while blocked by
316    /// [`sigmask`](Self::sigmask). They must be unblocked so that they are
317    /// caught and made available from this function.
318    fn caught_signals(&mut self) -> Vec<signal::Number>;
319
320    /// Sends a signal.
321    ///
322    /// This is a thin wrapper around the `kill` system call. If `signal` is
323    /// `None`, permission to send a signal is checked, but no signal is sent.
324    ///
325    /// The virtual system version of this function blocks the calling thread if
326    /// the signal stops or terminates the current process, hence returning a
327    /// future. See [`VirtualSystem::kill`] for details.
328    fn kill(&mut self, target: Pid, signal: Option<signal::Number>) -> FlexFuture<Result<()>>;
329
330    /// Sends a signal to the current process.
331    ///
332    /// This is a thin wrapper around the `raise` system call.
333    ///
334    /// The virtual system version of this function blocks the calling thread if
335    /// the signal stops or terminates the current process, hence returning a
336    /// future. See [`VirtualSystem::kill`] for details.
337    fn raise(&mut self, signal: signal::Number) -> FlexFuture<Result<()>>;
338
339    /// Waits for a next event.
340    ///
341    /// This is a low-level function used internally by
342    /// [`SharedSystem::select`]. You should not call this function directly, or
343    /// you will disrupt the behavior of `SharedSystem`. The description below
344    /// applies if you want to do everything yourself without depending on
345    /// `SharedSystem`.
346    ///
347    /// This function blocks the calling thread until one of the following
348    /// condition is met:
349    ///
350    /// - An FD in `readers` becomes ready for reading.
351    /// - An FD in `writers` becomes ready for writing.
352    /// - The specified `timeout` duration has passed.
353    /// - A signal handler catches a signal.
354    ///
355    /// When this function returns an `Ok`, FDs that are not ready for reading
356    /// and writing are removed from `readers` and `writers`, respectively. The
357    /// return value will be the number of FDs left in `readers` and `writers`.
358    ///
359    /// If `readers` and `writers` contain an FD that is not open for reading
360    /// and writing, respectively, this function will fail with `EBADF`. In this
361    /// case, you should remove the FD from `readers` and `writers` and try
362    /// again.
363    ///
364    /// If `signal_mask` is `Some` list of signals, it is used as the signal
365    /// blocking mask while waiting and restored when the function returns.
366    fn select(
367        &mut self,
368        readers: &mut Vec<Fd>,
369        writers: &mut Vec<Fd>,
370        timeout: Option<Duration>,
371        signal_mask: Option<&[signal::Number]>,
372    ) -> Result<c_int>;
373
374    /// Returns the session ID of the specified process.
375    ///
376    /// If `pid` is `Pid(0)`, this function returns the session ID of the
377    /// current process.
378    fn getsid(&self, pid: Pid) -> Result<Pid>;
379
380    /// Returns the process ID of the current process.
381    #[must_use]
382    fn getpid(&self) -> Pid;
383
384    /// Returns the process ID of the parent process.
385    #[must_use]
386    fn getppid(&self) -> Pid;
387
388    /// Returns the process group ID of the current process.
389    #[must_use]
390    fn getpgrp(&self) -> Pid;
391
392    /// Modifies the process group ID of a process.
393    ///
394    /// This is a thin wrapper around the `setpgid` system call.
395    fn setpgid(&mut self, pid: Pid, pgid: Pid) -> Result<()>;
396
397    /// Returns the current foreground process group ID.
398    ///
399    /// This is a thin wrapper around the `tcgetpgrp` system call.
400    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid>;
401
402    /// Switches the foreground process group.
403    ///
404    /// This is a thin wrapper around the `tcsetpgrp` system call.
405    fn tcsetpgrp(&mut self, fd: Fd, pgid: Pid) -> Result<()>;
406
407    /// Creates a new child process.
408    ///
409    /// This is a thin wrapper around the `fork` system call. Users of `Env`
410    /// should not call it directly. Instead, use [`Subshell`] so that the
411    /// environment can condition the state of the child process before it
412    /// starts running.
413    ///
414    /// Because we need the parent environment to create the child environment,
415    /// this method cannot initiate the child task directly. Instead, it returns
416    /// a [`ChildProcessStarter`] function that takes the parent environment and
417    /// the child task. The caller must call the starter to make sure the parent
418    /// and child processes perform correctly after forking.
419    fn new_child_process(&mut self) -> Result<ChildProcessStarter>;
420
421    /// Reports updated status of a child process.
422    ///
423    /// This is a low-level function used internally by
424    /// [`Env::wait_for_subshell`]. You should not call this function directly,
425    /// or you will disrupt the behavior of `Env`. The description below applies
426    /// if you want to do everything yourself without depending on `Env`.
427    ///
428    /// This function performs
429    /// `waitpid(target, ..., WUNTRACED | WCONTINUED | WNOHANG)`.
430    /// Despite the name, this function does not block: it returns the result
431    /// immediately.
432    ///
433    /// This function returns a pair of the process ID and the process state if
434    /// a process matching `target` is found and its state has changed. If all
435    /// the processes matching `target` have not changed their states, this
436    /// function returns `Ok(None)`. If an error occurs, this function returns
437    /// `Err(_)`.
438    fn wait(&mut self, target: Pid) -> Result<Option<(Pid, ProcessState)>>;
439
440    // TODO Consider passing raw pointers for optimization
441    /// Replaces the current process with an external utility.
442    ///
443    /// This is a thin wrapper around the `execve` system call.
444    fn execve(
445        &mut self,
446        path: &CStr,
447        args: &[CString],
448        envs: &[CString],
449    ) -> FlexFuture<Result<Infallible>>;
450
451    /// Terminates the current process.
452    ///
453    /// This function is a thin wrapper around the `_exit` system call.
454    fn exit(&mut self, exit_status: ExitStatus) -> FlexFuture<Infallible>;
455
456    /// Returns the current working directory path.
457    fn getcwd(&self) -> Result<PathBuf>;
458
459    /// Changes the working directory.
460    fn chdir(&mut self, path: &CStr) -> Result<()>;
461
462    /// Returns the real user ID of the current process.
463    fn getuid(&self) -> Uid;
464
465    /// Returns the effective user ID of the current process.
466    fn geteuid(&self) -> Uid;
467
468    /// Returns the real group ID of the current process.
469    fn getgid(&self) -> Gid;
470
471    /// Returns the effective group ID of the current process.
472    fn getegid(&self) -> Gid;
473
474    /// Returns the home directory path of the given user.
475    ///
476    /// Returns `Ok(None)` if the user is not found.
477    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>>;
478
479    /// Returns the standard `$PATH` value where all standard utilities are
480    /// expected to be found.
481    ///
482    /// This is a thin wrapper around the `confstr(_CS_PATH, …)`.
483    fn confstr_path(&self) -> Result<UnixString>;
484
485    /// Returns the path to the shell executable.
486    ///
487    /// If possible, this function should return the path to the current shell
488    /// executable. Otherwise, it should return the path to the default POSIX
489    /// shell.
490    fn shell_path(&self) -> CString;
491
492    /// Returns the limits for the specified resource.
493    ///
494    /// This function returns a pair of the soft and hard limits for the given
495    /// resource. The soft limit is the current limit, and the hard limit is the
496    /// maximum value that the soft limit can be set to.
497    ///
498    /// When no limit is set, the limit value is [`INFINITY`].
499    ///
500    /// This is a thin wrapper around the `getrlimit` system call.
501    ///
502    /// [`INFINITY`]: self::resource::INFINITY
503    fn getrlimit(&self, resource: Resource) -> Result<LimitPair>;
504
505    /// Sets the limits for the specified resource.
506    ///
507    /// Specify [`INFINITY`] as the limit value to remove the limit.
508    ///
509    /// This is a thin wrapper around the `setrlimit` system call.
510    ///
511    /// [`INFINITY`]: self::resource::INFINITY
512    fn setrlimit(&mut self, resource: Resource, limits: LimitPair) -> Result<()>;
513}
514
515/// Set of consumed CPU time
516///
517/// This structure contains four CPU time values, all in seconds.
518///
519/// This structure is returned by [`System::times`].
520#[derive(Clone, Copy, Debug, Default, PartialEq)]
521pub struct Times {
522    /// User CPU time consumed by the current process
523    pub self_user: f64,
524    /// System CPU time consumed by the current process
525    pub self_system: f64,
526    /// User CPU time consumed by the children of the current process
527    pub children_user: f64,
528    /// System CPU time consumed by the children of the current process
529    pub children_system: f64,
530}
531
532/// Operation applied to the signal blocking mask
533#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
534#[non_exhaustive]
535pub enum SigmaskOp {
536    /// Add signals to the mask (`SIG_BLOCK`)
537    Add,
538    /// Remove signals from the mask (`SIG_UNBLOCK`)
539    Remove,
540    /// Set the mask to the given signals (`SIG_SETMASK`)
541    Set,
542}
543
544/// How the shell process responds to a signal
545#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
546pub enum Disposition {
547    /// Perform the default action for the signal.
548    ///
549    /// The default action depends on the signal. For example, `SIGINT` causes
550    /// the process to terminate, and `SIGTSTP` causes the process to stop.
551    #[default]
552    Default,
553    /// Ignore the signal.
554    Ignore,
555    /// Catch the signal.
556    Catch,
557}
558
559/// Task executed in a child process
560///
561/// This is an argument passed to a [`ChildProcessStarter`]. The task is
562/// executed in a child process initiated by the starter. The environment passed
563/// to the task is a clone of the parent environment, but it has a different
564/// process ID than the parent.
565///
566/// Note that the output type of the task is `Infallible`. This is to ensure that
567/// the task [exits](System::exit) cleanly or [kills](System::kill) itself with
568/// a signal.
569pub type ChildProcessTask =
570    Box<dyn for<'a> FnOnce(&'a mut Env) -> Pin<Box<dyn Future<Output = Infallible> + 'a>>>;
571
572/// Abstract function that starts a child process
573///
574/// [`System::new_child_process`] returns a child process starter. You need to
575/// pass the parent environment and a task to the starter to complete the child
576/// process creation. The starter provides a unified interface that hides the
577/// differences between [`RealSystem`] and [`VirtualSystem`].
578///
579/// [`RealSystem::new_child_process`] performs a `fork` system call and returns
580/// a starter in the parent and child processes. When the starter is called in
581/// the parent, it just returns the child process ID. The starter in the child
582/// process runs the task and exits the process with the exit status of the
583/// task.
584///
585/// [`VirtualSystem::new_child_process`] does ont create a real child process.
586/// Instead, the starter runs the task concurrently in the current process using
587/// the executor contained in the system. A new [`Process`](virtual::Process) is
588/// added to the system to represent the child process. The starter returns its
589/// process ID.
590///
591/// This function only starts the child, which continues to run asynchronously
592/// after the function returns its PID. To wait for the child to finish and
593/// obtain its exit status, use [`System::wait`].
594pub type ChildProcessStarter = Box<dyn FnOnce(&mut Env, ChildProcessTask) -> Pid>;
595
596/// Extension for [`System`]
597///
598/// This trait provides some extension methods for `System`.
599pub trait SystemEx: System {
600    /// Moves a file descriptor to [`MIN_INTERNAL_FD`] or larger.
601    ///
602    /// This function can be used to make sure a file descriptor used by the
603    /// shell does not conflict with file descriptors used by the user.
604    /// [`MIN_INTERNAL_FD`] is the minimum file descriptor number the shell
605    /// uses internally. This function moves the file descriptor to a number
606    /// larger than or equal to [`MIN_INTERNAL_FD`].
607    ///
608    /// If the given file descriptor is less than [`MIN_INTERNAL_FD`], this
609    /// function duplicates the file descriptor with [`System::dup`] and closes
610    /// the original one. Otherwise, this function does nothing.
611    ///
612    /// The new file descriptor will have the CLOEXEC flag set when it is
613    /// dupped. Note that, if the original file descriptor has the CLOEXEC flag
614    /// unset and is already larger than or equal to [`MIN_INTERNAL_FD`], this
615    /// function will not set the CLOEXEC flag for the returned file descriptor.
616    ///
617    /// This function returns the new file descriptor on success. On error, it
618    /// closes the original file descriptor and returns the error.
619    fn move_fd_internal(&mut self, from: Fd) -> Result<Fd> {
620        if from >= MIN_INTERNAL_FD {
621            return Ok(from);
622        }
623
624        let new = self.dup(from, MIN_INTERNAL_FD, FdFlag::CloseOnExec.into());
625        self.close(from).ok();
626        new
627    }
628
629    /// Tests if a file descriptor is a pipe.
630    fn fd_is_pipe(&self, fd: Fd) -> bool {
631        self.fstat(fd)
632            .is_ok_and(|stat| stat.r#type == FileType::Fifo)
633    }
634
635    /// Switches the foreground process group with SIGTTOU blocked.
636    ///
637    /// This is a convenience function to change the foreground process group
638    /// safely. If you call [`tcsetpgrp`](System::tcsetpgrp) from a background
639    /// process, the process is stopped by SIGTTOU by default. To prevent this
640    /// effect, SIGTTOU must be blocked or ignored when `tcsetpgrp` is called.
641    /// This function uses [`sigmask`](System::sigmask) to block SIGTTOU before
642    /// calling [`tcsetpgrp`](System::tcsetpgrp) and also to restore the
643    /// original signal mask after `tcsetpgrp`.
644    ///
645    /// Use [`tcsetpgrp_without_block`](Self::tcsetpgrp_without_block) if you
646    /// need to make sure the shell is in the foreground before changing the
647    /// foreground job.
648    fn tcsetpgrp_with_block(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
649        let sigttou = self
650            .signal_number_from_name(signal::Name::Ttou)
651            .ok_or(Errno::EINVAL)?;
652        let mut old_mask = Vec::new();
653
654        self.sigmask(Some((SigmaskOp::Add, &[sigttou])), Some(&mut old_mask))?;
655
656        let result = self.tcsetpgrp(fd, pgid);
657
658        let result_2 = self.sigmask(Some((SigmaskOp::Set, &old_mask)), None);
659
660        result.and(result_2)
661    }
662
663    /// Switches the foreground process group with the default SIGTTOU settings.
664    ///
665    /// This is a convenience function to ensure the shell has been in the
666    /// foreground and optionally change the foreground process group. This
667    /// function calls [`sigaction`](System::sigaction) to restore the action
668    /// for SIGTTOU to the default disposition (which is to suspend the shell
669    /// process), [`sigmask`](System::sigmask) to unblock SIGTTOU, and
670    /// [`tcsetpgrp`](System::tcsetpgrp) to modify the foreground job. If the
671    /// calling process is not in the foreground, `tcsetpgrp` will suspend the
672    /// process with SIGTTOU until another job-controlling process resumes it in
673    /// the foreground. After `tcsetpgrp` completes, this function calls
674    /// `sigmask` and `sigaction` to restore the original state.
675    ///
676    /// Note that if `pgid` is the process group ID of the current process, this
677    /// function does not change the foreground job, but the process is still
678    /// subject to suspension if it has not been in the foreground.
679    ///
680    /// Use [`tcsetpgrp_with_block`](Self::tcsetpgrp_with_block) to change the
681    /// job even if the current shell is not in the foreground.
682    fn tcsetpgrp_without_block(&mut self, fd: Fd, pgid: Pid) -> Result<()> {
683        let sigttou = self
684            .signal_number_from_name(signal::Name::Ttou)
685            .ok_or(Errno::EINVAL)?;
686        match self.sigaction(sigttou, Disposition::Default) {
687            Err(e) => Err(e),
688            Ok(old_handling) => {
689                let mut old_mask = Vec::new();
690                let result = match self
691                    .sigmask(Some((SigmaskOp::Remove, &[sigttou])), Some(&mut old_mask))
692                {
693                    Err(e) => Err(e),
694                    Ok(()) => {
695                        let result = self.tcsetpgrp(fd, pgid);
696
697                        let result_2 = self.sigmask(Some((SigmaskOp::Set, &old_mask)), None);
698
699                        result.and(result_2)
700                    }
701                };
702
703                let result_2 = self.sigaction(sigttou, old_handling).map(drop);
704
705                result.and(result_2)
706            }
707        }
708    }
709
710    /// Returns the signal name for the signal number.
711    ///
712    /// This function returns the signal name for the given signal number.
713    ///
714    /// If the signal number is invalid, this function panics. It may occur if
715    /// the number is from a different system or was created without checking
716    /// the validity.
717    #[must_use]
718    fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
719        self.validate_signal(number.as_raw()).unwrap().0
720    }
721
722    /// Terminates the current process with the given exit status, possibly
723    /// sending a signal to kill the process.
724    ///
725    /// If the exit status represents a signal that killed the last executed
726    /// command, this function sends the signal to the current process to
727    /// propagate the signal to the parent process. Otherwise, this function
728    /// terminates the process with the given exit status.
729    fn exit_or_raise(&mut self, exit_status: ExitStatus) -> impl Future<Output = Infallible> {
730        async fn maybe_raise<S: System + ?Sized>(
731            exit_status: ExitStatus,
732            system: &mut S,
733        ) -> Option<Infallible> {
734            let signal = exit_status.to_signal(system, /* exact */ true)?;
735
736            if !matches!(SignalEffect::of(signal.0), SignalEffect::Terminate { .. }) {
737                return None;
738            }
739
740            // Disable core dump
741            system
742                .setrlimit(Resource::CORE, LimitPair { soft: 0, hard: 0 })
743                .ok()?;
744
745            if signal.0 != signal::Name::Kill {
746                // Reset signal disposition
747                system.sigaction(signal.1, Disposition::Default).ok()?;
748            }
749
750            // Unblock the signal
751            system
752                .sigmask(Some((SigmaskOp::Remove, &[signal.1])), None)
753                .ok()?;
754
755            // Send the signal to the current process
756            system.raise(signal.1).await.ok()?;
757
758            None
759        }
760
761        async move {
762            maybe_raise(exit_status, self).await;
763            self.exit(exit_status).await
764        }
765    }
766}
767
768impl<T: System + ?Sized> SystemEx for T {}