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//! API declarations and implementations for system-managed parts of the environment
18//!
19//! This module defines the [`System`] trait, which provides an interface to
20//! interact with the underlying system. It is a subtrait of various other traits
21//! that define specific functionalities, such as file system operations, process
22//! management, signal handling, and resource limit management. The following
23//! traits are included as subtraits of `System`:
24//!
25//! - [`CaughtSignals`]: Declares the `caught_signals` method for retrieving
26//! caught signals.
27//! - [`Chdir`]: Declares the `chdir` method for changing the current
28//! working directory.
29//! - [`Clock`]: Declares the `now` method for getting the current time.
30//! - [`Close`]: Declares the `close` method for closing file descriptors.
31//! - [`Dup`]: Declares the `dup` and `dup2` methods for duplicating file
32//! descriptors.
33//! - [`Exec`]: Declares the `execve` method for executing new programs.
34//! - [`Exit`]: Declares the `exit` method for terminating the current
35//! process.
36//! - [`Fcntl`]: Declares the `ofd_access`, `get_and_set_nonblocking`,
37//! `fcntl_getfd`, and `fcntl_setfd` methods for `fcntl`-related operations.
38//! - [`Fork`]: Declares a method for creating new child processes.
39//! - [`Fstat`]: Declares `fstat` and `fstatat` methods for getting file
40//! metadata and provides a default implementation of `is_directory`.
41//! - [`GetCwd`]: Declares the `getcwd` method for getting the current
42//! working directory.
43//! - [`GetPid`]: Declares the `getpid` and other methods for getting process IDs
44//! and other attributes.
45//! - [`GetPw`]: Declares methods for getting user information.
46//! - [`GetUid`]: Declares the `getuid`, `geteuid`, `getgid`, and
47//! `getegid` methods for getting user and group IDs.
48//! - [`IsExecutableFile`]: Declares the `is_executable_file` method for checking
49//! if a file is executable.
50//! - [`Isatty`]: Declares the `isatty` method for testing if a file descriptor is
51//! associated with a terminal device.
52//! - [`Open`]: Declares the `open` and other methods for opening files.
53//! - [`Pipe`]: Declares the `pipe` method for creating pipes.
54//! - [`Read`]: Declares the `read` method for reading from file descriptors.
55//! - [`Seek`]: Declares the `lseek` method for seeking within file
56//! descriptors.
57//! - [`Select`]: Declares the `select` method for waiting on multiple file
58//! descriptors and signals.
59//! - [`SendSignal`]: Declares the `kill` and `raise` methods for sending signals
60//! to processes.
61//! - [`SetPgid`]: Declares the `setpgid` method for setting process group IDs.
62//! - [`ShellPath`]: Declares the `shell_path` method for getting the path to
63//! the shell executable.
64//! - [`Sigaction`]: Declares methods for managing signal dispositions.
65//! - [`Sigmask`]: Declares the `sigmask` method for managing signal masks.
66//! - [`Signals`]: Declares the `signal_number_from_name` and
67//! `validate_signal` methods for converting between signal names and numbers.
68//! - [`TcGetPgrp`]: Declares the `tcgetpgrp` method for getting the
69//! foreground process group ID of a terminal.
70//! - [`TcSetPgrp`]: Declares the `tcsetpgrp` method for setting the
71//! foreground process group ID of a terminal.
72//! - [`Times`]: Declares the `times` method for getting CPU times.
73//! - [`Umask`]: Declares the `umask` method for setting the file mode
74//! creation mask.
75//! - [`Wait`]: Declares the `wait` method for waiting for child processes.
76//! - [`Write`]: Declares the `write` method for writing to file
77//! descriptors.
78//! - [`resource::GetRlimit`]: Declares the `getrlimit` method for
79//! retrieving resource limits.
80//! - [`resource::SetRlimit`]: Declares the `setrlimit` method for
81//! setting resource limits.
82//!
83//! There are two main implementors of the `System` trait:
84//!
85//! - `RealSystem`: An implementation that interacts with the actual
86//! underlying system (see the [`real`] module).
87//! - `VirtualSystem`: An implementation that simulates system behavior
88//! for testing purposes (see the [`virtual`] module).
89//!
90//! Additionally, there is the [`SharedSystem`] implementor that wraps
91//! another `System` instance to provide asynchronous methods.
92//!
93//! User code should generally depend only on specific subtraits of `System`
94//! rather than `System` itself. This allows for more modular and testable code.
95//! For example, code that only needs to write to file descriptors can depend
96//! on the `Write` trait alone.
97
98mod errno;
99mod file_system;
100mod future;
101mod io;
102mod process;
103#[cfg(unix)]
104pub mod real;
105pub mod resource;
106mod select;
107mod shared;
108mod signal;
109mod sysconf;
110mod terminal;
111mod time;
112mod user;
113pub mod r#virtual;
114
115pub use self::errno::Errno;
116pub use self::errno::RawErrno;
117pub use self::errno::Result;
118pub use self::file_system::{
119 AT_FDCWD, Chdir, Dir, DirEntry, FileType, Fstat, GetCwd, IsExecutableFile, Mode, OfdAccess,
120 Open, OpenFlag, RawMode, Seek, Stat, Umask,
121};
122pub use self::future::FlexFuture;
123pub use self::io::{Close, Dup, Fcntl, FdFlag, Pipe, Read, Write};
124pub use self::process::{
125 ChildProcessStarter, ChildProcessTask, Exec, Exit, Fork, GetPid, SetPgid, Wait,
126};
127#[cfg(all(doc, unix))]
128use self::real::RealSystem;
129use self::resource::{GetRlimit, LimitPair, Resource, SetRlimit};
130pub use self::select::Select;
131use self::select::SelectSystem;
132use self::select::SignalStatus;
133pub use self::shared::SharedSystem;
134pub use self::signal::{
135 CaughtSignals, Disposition, SendSignal, Sigaction, Sigmask, SigmaskOp, Signals,
136};
137pub use self::sysconf::{ShellPath, Sysconf};
138pub use self::terminal::{Isatty, TcGetPgrp, TcSetPgrp};
139pub use self::time::{Clock, CpuTimes, Times};
140pub use self::user::{GetPw, GetUid, Gid, RawGid, RawUid, Uid};
141#[cfg(doc)]
142use self::r#virtual::VirtualSystem;
143use crate::io::Fd;
144#[cfg(doc)]
145use crate::io::MIN_INTERNAL_FD;
146use crate::job::Pid;
147use crate::path::Path;
148use crate::path::PathBuf;
149use crate::semantics::ExitStatus;
150use crate::str::UnixString;
151#[cfg(doc)]
152use crate::subshell::Subshell;
153use crate::trap::SignalSystem;
154use std::convert::Infallible;
155use std::fmt::Debug;
156
157/// API to the system-managed parts of the environment.
158///
159/// The `System` trait defines a collection of methods to access the underlying
160/// operating system from the shell as an application program. There are two
161/// substantial implementors for this trait: [`RealSystem`] and
162/// [`VirtualSystem`]. Another implementor is [`SharedSystem`], which wraps a
163/// `System` instance to extend the interface with asynchronous methods.
164#[deprecated(
165 note = "use smaller, more specialized traits declared in the `system` module instead",
166 since = "0.11.0"
167)]
168pub trait System:
169 CaughtSignals
170 + Chdir
171 + Clock
172 + Close
173 + Debug
174 + Dup
175 + Exec
176 + Exit
177 + Fcntl
178 + Fork
179 + Fstat
180 + GetCwd
181 + GetPid
182 + GetPw
183 + GetRlimit
184 + GetUid
185 + IsExecutableFile
186 + Isatty
187 + Open
188 + Pipe
189 + Read
190 + Seek
191 + Select
192 + SendSignal
193 + SetPgid
194 + SetRlimit
195 + ShellPath
196 + Sigaction
197 + Sigmask
198 + Signals
199 + Sysconf
200 + TcGetPgrp
201 + TcSetPgrp
202 + Times
203 + Umask
204 + Wait
205 + Write
206{
207}
208
209#[allow(deprecated)]
210impl<T> System for T where
211 T: CaughtSignals
212 + Chdir
213 + Clock
214 + Close
215 + Debug
216 + Dup
217 + Exec
218 + Exit
219 + Fcntl
220 + Fork
221 + Fstat
222 + GetCwd
223 + GetPid
224 + GetPw
225 + GetRlimit
226 + GetUid
227 + IsExecutableFile
228 + Isatty
229 + Open
230 + Pipe
231 + Read
232 + Seek
233 + Select
234 + SendSignal
235 + SetPgid
236 + SetRlimit
237 + ShellPath
238 + Sigaction
239 + Sigmask
240 + Signals
241 + Sysconf
242 + TcGetPgrp
243 + TcSetPgrp
244 + Times
245 + Umask
246 + Wait
247 + Write
248{
249}
250
251/// Extension for [`System`]
252///
253/// This trait provides some extension methods for `System`.
254#[allow(deprecated)]
255#[deprecated(
256 note = "use functions in the `yash-env::io` and `yash-env::job` modules instead",
257 since = "0.11.0"
258)]
259pub trait SystemEx: System {
260 /// Moves a file descriptor to [`MIN_INTERNAL_FD`] or larger.
261 ///
262 /// This function can be used to make sure a file descriptor used by the
263 /// shell does not conflict with file descriptors used by the user.
264 /// [`MIN_INTERNAL_FD`] is the minimum file descriptor number the shell
265 /// uses internally. This function moves the file descriptor to a number
266 /// larger than or equal to [`MIN_INTERNAL_FD`].
267 ///
268 /// If the given file descriptor is less than [`MIN_INTERNAL_FD`], this
269 /// function duplicates the file descriptor with [`Dup::dup`] and closes
270 /// the original one. Otherwise, this function does nothing.
271 ///
272 /// The new file descriptor will have the CLOEXEC flag set when it is
273 /// dupped. Note that, if the original file descriptor has the CLOEXEC flag
274 /// unset and is already larger than or equal to [`MIN_INTERNAL_FD`], this
275 /// function will not set the CLOEXEC flag for the returned file descriptor.
276 ///
277 /// This function returns the new file descriptor on success. On error, it
278 /// closes the original file descriptor and returns the error.
279 #[deprecated(
280 note = "use `yash_env::io::move_fd_internal` instead",
281 since = "0.11.0"
282 )]
283 fn move_fd_internal(&mut self, from: Fd) -> Result<Fd> {
284 crate::io::move_fd_internal(self, from)
285 }
286
287 /// Tests if a file descriptor is a pipe.
288 #[deprecated(
289 note = "use `yash_env::system::Fstat::fd_is_pipe` instead",
290 since = "0.11.0"
291 )]
292 fn fd_is_pipe(&self, fd: Fd) -> bool {
293 self.fstat(fd)
294 .is_ok_and(|stat| stat.r#type == FileType::Fifo)
295 }
296
297 /// Switches the foreground process group with SIGTTOU blocked.
298 ///
299 /// This is a convenience function to change the foreground process group
300 /// safely. If you call [`TcSetPgrp::tcsetpgrp`] from a background process,
301 /// the process is stopped by SIGTTOU by default. To prevent this effect,
302 /// SIGTTOU must be blocked or ignored when `tcsetpgrp` is called. This
303 /// function uses [`Sigmask::sigmask`] to block SIGTTOU before calling
304 /// `tcsetpgrp` and also to restore the original signal mask after
305 /// `tcsetpgrp`.
306 ///
307 /// Use [`tcsetpgrp_without_block`](Self::tcsetpgrp_without_block) if you
308 /// need to make sure the shell is in the foreground before changing the
309 /// foreground job.
310 #[deprecated(
311 note = "use `yash_env::job::tcsetpgrp_with_block` instead",
312 since = "0.11.0"
313 )]
314 fn tcsetpgrp_with_block(&mut self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> {
315 crate::job::tcsetpgrp_with_block(self, fd, pgid)
316 }
317
318 /// Switches the foreground process group with the default SIGTTOU settings.
319 ///
320 /// This is a convenience function to ensure the shell has been in the
321 /// foreground and optionally change the foreground process group. This
322 /// function calls [`Sigaction::sigaction`] to restore the action for
323 /// SIGTTOU to the default disposition (which is to suspend the shell
324 /// process), [`Sigmask::sigmask`] to unblock SIGTTOU, and
325 /// [`TcSetPgrp::tcsetpgrp`] to modify the foreground job. If the calling
326 /// process is not in the foreground, `tcsetpgrp` will suspend the process
327 /// with SIGTTOU until another job-controlling process resumes it in the
328 /// foreground. After `tcsetpgrp` completes, this function calls `sigmask`
329 /// and `sigaction` to restore the original state.
330 ///
331 /// Note that if `pgid` is the process group ID of the current process, this
332 /// function does not change the foreground job, but the process is still
333 /// subject to suspension if it has not been in the foreground.
334 ///
335 /// Use [`tcsetpgrp_with_block`](Self::tcsetpgrp_with_block) to change the
336 /// job even if the current shell is not in the foreground.
337 #[deprecated(
338 note = "use `yash_env::job::tcsetpgrp_without_block` instead",
339 since = "0.11.0"
340 )]
341 fn tcsetpgrp_without_block(&mut self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> {
342 crate::job::tcsetpgrp_without_block(self, fd, pgid)
343 }
344
345 /// Returns the signal name for the signal number.
346 ///
347 /// This function returns the signal name for the given signal number.
348 ///
349 /// If the signal number is invalid, this function panics. It may occur if
350 /// the number is from a different system or was created without checking
351 /// the validity.
352 #[deprecated(
353 note = "use `yash_env::system::Signals::signal_name_from_number` instead",
354 since = "0.11.0"
355 )]
356 #[must_use]
357 fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
358 self.validate_signal(number.as_raw()).unwrap().0
359 }
360
361 /// Terminates the current process with the given exit status, possibly
362 /// sending a signal to kill the process.
363 ///
364 /// If the exit status represents a signal that killed the last executed
365 /// command, this function sends the signal to the current process to
366 /// propagate the signal to the parent process. Otherwise, this function
367 /// terminates the process with the given exit status.
368 #[deprecated(
369 note = "use `yash_env::semantics::exit_or_raise` instead",
370 since = "0.11.0"
371 )]
372 fn exit_or_raise(&mut self, exit_status: ExitStatus) -> impl Future<Output = Infallible> {
373 async move { crate::semantics::exit_or_raise(self, exit_status).await }
374 }
375}
376
377#[allow(deprecated)]
378impl<T: System + ?Sized> SystemEx for T {}