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 {}