yash_env/system/process.rs
1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2025 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//! Items related to process management
18
19use super::{ExitStatus, Result, Signals};
20use crate::Env;
21#[cfg(all(doc, unix))]
22use crate::RealSystem;
23#[cfg(doc)]
24use crate::VirtualSystem;
25use crate::job::Pid;
26use crate::job::ProcessState;
27use std::convert::Infallible;
28use std::ffi::{CStr, CString};
29use std::pin::Pin;
30
31/// Trait for getting the current process ID and other process-related information
32pub trait GetPid {
33 /// Returns the process ID of the current process.
34 ///
35 /// This method represents the [`getpid` system
36 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpid.html).
37 #[must_use]
38 fn getpid(&self) -> Pid;
39
40 /// Returns the process ID of the parent process.
41 ///
42 /// This method represents the [`getppid` system
43 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/getppid.html).
44 #[must_use]
45 fn getppid(&self) -> Pid;
46
47 /// Returns the process group ID of the current process.
48 ///
49 /// This method represents the [`getpgrp` system
50 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgrp.html).
51 #[must_use]
52 fn getpgrp(&self) -> Pid;
53
54 /// Returns the session ID of the specified process.
55 ///
56 /// If `pid` is `Pid(0)`, this function returns the session ID of the
57 /// current process.
58 fn getsid(&self, pid: Pid) -> Result<Pid>;
59}
60
61/// Trait for modifying the process group ID of processes
62pub trait SetPgid {
63 /// Modifies the process group ID of a process.
64 ///
65 /// This method represents the [`setpgid` system
66 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpgid.html).
67 ///
68 /// `pid` specifies the process whose process group ID is to be changed. If `pid` is
69 /// `Pid(0)`, the current process is used.
70 /// `pgid` specifies the new process group ID to be set. If `pgid` is
71 /// `Pid(0)`, the process ID of the specified process is used.
72 fn setpgid(&self, pid: Pid, pgid: Pid) -> Result<()>;
73}
74
75type PinFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
76
77/// Task executed in a child process
78///
79/// This is an argument passed to a [`ChildProcessStarter`]. The task is
80/// executed in a child process initiated by the starter. The environment passed
81/// to the task is a clone of the parent environment, but it has a different
82/// process ID than the parent.
83///
84/// Note that the output type of the task is `Infallible`. This is to ensure
85/// that the task [exits](super::Exit::exit) cleanly or
86/// [kills](super::SendSignal::kill) itself with a signal.
87pub type ChildProcessTask<S> = Box<dyn for<'a> FnOnce(&'a mut Env<S>) -> PinFuture<'a, Infallible>>;
88
89/// Abstract function that starts a child process
90///
91/// [`Fork::new_child_process`] returns a child process starter. You need to
92/// pass the parent environment and a task to the starter to complete the child
93/// process creation. The starter provides a unified interface that hides the
94/// differences between [`RealSystem`] and [`VirtualSystem`].
95///
96/// [`RealSystem::new_child_process`] performs a `fork` system call and returns
97/// a starter in the parent and child processes. When the starter is called in
98/// the parent, it just returns the child process ID. The starter in the child
99/// process runs the task and exits the process with the exit status of the
100/// task.
101///
102/// [`VirtualSystem::new_child_process`] does not create a real child process.
103/// Instead, the starter runs the task concurrently in the current process using
104/// the executor contained in the system. A new
105/// [`Process`](super::virtual::Process) is added to the system to represent the
106/// child process. The starter returns its process ID.
107///
108/// This function only starts the child, which continues to run asynchronously
109/// after the function returns its PID. To wait for the child to finish and
110/// obtain its exit status, use [`wait`](Wait::wait).
111pub type ChildProcessStarter<S> = Box<dyn FnOnce(&mut Env<S>, ChildProcessTask<S>) -> Pid>;
112
113/// Trait for spawning new processes
114pub trait Fork {
115 // XXX: This method needs refactoring! (#662)
116 /// Creates a new child process.
117 ///
118 /// This is a wrapper around the [`fork` system
119 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html).
120 /// Users of [`Env`] should not call it directly. Instead, use
121 /// [`Subshell`](crate::subshell::Subshell) so that the environment can
122 /// condition the state of the child process before it starts running.
123 ///
124 /// Because we need the parent environment to create the child environment,
125 /// this method cannot initiate the child task directly. Instead, it returns
126 /// a [`ChildProcessStarter`] function that takes the parent environment and
127 /// the child task. The caller must call the starter to make sure the parent
128 /// and child processes perform correctly after forking.
129 fn new_child_process(&self) -> Result<ChildProcessStarter<Self>>
130 where
131 Self: Sized;
132}
133
134/// Trait for waiting for child processes
135pub trait Wait: Signals {
136 /// Reports updated status of a child process.
137 ///
138 /// This is a low-level function used internally by
139 /// [`Env::wait_for_subshell`](crate::Env::wait_for_subshell). You should
140 /// not call this function directly, or you will disrupt the behavior of
141 /// `Env`. The description below applies if you want to do everything
142 /// yourself without depending on `Env`.
143 ///
144 /// This function performs
145 /// `waitpid(target, ..., WUNTRACED | WCONTINUED | WNOHANG)`.
146 /// Despite the name, this function does not block: it returns the result
147 /// immediately.
148 ///
149 /// This function returns a pair of the process ID and the process state if
150 /// a process matching `target` is found and its state has changed. If all
151 /// the processes matching `target` have not changed their states, this
152 /// function returns `Ok(None)`. If an error occurs, this function returns
153 /// `Err(_)`.
154 fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>>;
155}
156
157/// Trait for executing a new program in the current process
158pub trait Exec {
159 // TODO Consider passing raw pointers for optimization
160 /// Replaces the current process with an external utility.
161 ///
162 /// This is a thin wrapper around the `execve` system call.
163 fn execve(
164 &self,
165 path: &CStr,
166 args: &[CString],
167 envs: &[CString],
168 ) -> impl Future<Output = Result<Infallible>> + use<Self>;
169}
170
171/// Trait for terminating the current process
172pub trait Exit {
173 /// Terminates the current process.
174 ///
175 /// This function is a thin wrapper around the [`_exit` system
176 /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/_exit.html).
177 fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<Self>;
178}