starnix_core/task/
zombie.rs1use crate::signals::syscalls::WaitingOptions;
6use crate::signals::{SignalDetail, SignalInfo};
7use crate::task::{
8 ExitStatus, Pid, PidTableGuard, ProcessSelector, Task, ThreadGroup, ThreadGroupStateRef,
9};
10use starnix_logging::log_warn;
11use starnix_types::ownership::{OwnedRef, Releasable};
12use starnix_types::stats::TaskTimeStats;
13use starnix_uapi::signals::{SIGCHLD, Signal};
14use starnix_uapi::{pid_t, uid_t};
15use std::sync::{Arc, Weak};
16
17#[derive(Debug)]
18pub struct ZombieProcess {
19 pub task: Arc<Task>,
20 pub pgid: Pid,
21 pub exit_signal: Option<Signal>,
22 pub state: ZombieState,
23
24 pub is_canonical: bool,
27}
28
29impl PartialEq for ZombieProcess {
30 fn eq(&self, other: &Self) -> bool {
31 self.task.pid == other.task.pid && self.is_canonical == other.is_canonical
33 }
34}
35
36impl Eq for ZombieProcess {}
37
38impl PartialOrd for ZombieProcess {
39 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
40 Some(self.cmp(other))
41 }
42}
43
44impl Ord for ZombieProcess {
45 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
46 (&self.task.pid, self.is_canonical).cmp(&(&other.task.pid, other.is_canonical))
47 }
48}
49
50impl ZombieProcess {
51 pub fn new(
52 task: Arc<Task>,
53 thread_group: ThreadGroupStateRef<'_>,
54 exit_status: ExitStatus,
55 exit_signal: Option<Signal>,
56 ) -> OwnedRef<Self> {
57 let time_stats = thread_group.base.time_stats() + thread_group.children_time_stats;
58 OwnedRef::new(ZombieProcess {
59 task,
60 pgid: thread_group.process_group.leader.clone(),
61 state: ZombieState { exit_status, time_stats },
62 exit_signal,
63 is_canonical: true,
64 })
65 }
66
67 pub fn pgid(&self) -> pid_t {
68 self.pgid.id
69 }
70
71 pub fn to_wait_result(&self) -> WaitResult {
72 WaitResult {
73 pid: self.task.pid.clone(),
74 uid: self.task.real_creds().uid,
75 zombie_state: self.state.clone(),
76 exit_signal: self.exit_signal,
77 }
78 }
79
80 pub fn as_artificial(&self) -> Self {
81 ZombieProcess {
82 task: self.task.clone(),
83 pgid: self.pgid.clone(),
84 state: self.state.clone(),
85 exit_signal: self.exit_signal,
86 is_canonical: false,
87 }
88 }
89
90 pub fn matches_selector(&self, selector: &ProcessSelector) -> bool {
91 match selector {
92 ProcessSelector::Any => true,
93 ProcessSelector::Pid(pid) => &self.task.pid == pid,
94 ProcessSelector::Pgid(pgid) => &self.pgid == pgid,
95 }
96 }
97
98 pub fn matches_selector_and_waiting_option(
99 &self,
100 selector: &ProcessSelector,
101 options: &WaitingOptions,
102 ) -> bool {
103 if !self.matches_selector(selector) {
104 return false;
105 }
106
107 if options.wait_for_all {
108 true
109 } else {
110 options.wait_for_clone == (self.exit_signal != Some(SIGCHLD))
113 }
114 }
115}
116
117pub trait ZombieReleaser {
123 fn remove_zombie(&mut self, pid: &Pid);
124}
125
126impl<'a> ZombieReleaser for PidTableGuard<'a> {
127 fn remove_zombie(&mut self, pid: &Pid) {
128 self.remove_zombie(pid);
129 }
130}
131
132impl Releasable for ZombieProcess {
133 type Context<'a> = &'a mut dyn ZombieReleaser;
134
135 fn release<'a>(self, pids: &'a mut dyn ZombieReleaser) {
136 if self.is_canonical {
137 pids.remove_zombie(&self.task.pid);
138 }
139 }
140}
141
142#[must_use = "Notifications must be explicitly delivered or discarded"]
160pub struct ZombieNotification {
161 pub recipient: Weak<ThreadGroup>,
163
164 pub zombie: OwnedRef<ZombieProcess>,
166}
167
168impl ZombieNotification {
169 pub fn new(recipient: Weak<ThreadGroup>, zombie: OwnedRef<ZombieProcess>) -> Self {
170 Self { recipient, zombie }
171 }
172
173 pub fn deliver(self, pids: &mut PidTableGuard<'_>) {
179 if let Some(parent) = self.recipient.upgrade() {
180 parent.do_zombie_notifications(self.zombie, pids);
181 } else {
182 log_warn!("Zombie {} reaped silently", self.zombie.task.get_pid());
183 self.zombie.release(pids);
184 }
185 }
186
187 pub fn discard(self, pids: &mut PidTableGuard<'_>) {
191 self.zombie.release(pids);
192 }
193}
194
195#[derive(Clone, Debug, PartialEq, Eq)]
196pub struct WaitResult {
197 pub pid: Pid,
198 pub uid: uid_t,
199 pub zombie_state: ZombieState,
200 pub exit_signal: Option<Signal>,
201}
202
203impl WaitResult {
204 pub fn as_signal_info(&self) -> SignalInfo {
206 SignalInfo::with_detail(
207 SIGCHLD,
208 self.zombie_state.exit_status.signal_info_code(),
209 SignalDetail::SIGCHLD {
210 pid: self.pid.clone(),
211 uid: self.uid,
212 status: self.zombie_state.exit_status.signal_info_status(),
213 },
214 )
215 }
216}
217
218#[derive(Clone, Debug, PartialEq, Eq)]
220pub struct ZombieState {
221 pub exit_status: ExitStatus,
222 pub time_stats: TaskTimeStats,
223}