Skip to main content

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