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::uid_t;
15use std::sync::{Arc, Weak};
16
17#[derive(Debug)]
18pub struct ZombieProcess {
19 pub task: Arc<Task>,
20 pub exit_signal: Option<Signal>,
21 pub state: ZombieState,
22
23 pub is_canonical: bool,
26}
27
28impl PartialEq for ZombieProcess {
29 fn eq(&self, other: &Self) -> bool {
30 self.task.pid == other.task.pid && self.is_canonical == other.is_canonical
32 }
33}
34
35impl Eq for ZombieProcess {}
36
37impl PartialOrd for ZombieProcess {
38 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
39 Some(self.cmp(other))
40 }
41}
42
43impl Ord for ZombieProcess {
44 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
45 (&self.task.pid, self.is_canonical).cmp(&(&other.task.pid, other.is_canonical))
46 }
47}
48
49impl ZombieProcess {
50 pub fn new(
51 task: Arc<Task>,
52 thread_group: ThreadGroupStateRef<'_>,
53 exit_status: ExitStatus,
54 exit_signal: Option<Signal>,
55 ) -> OwnedRef<Self> {
56 let time_stats = thread_group.base.time_stats() + thread_group.children_time_stats;
57 OwnedRef::new(ZombieProcess {
58 task,
59 state: ZombieState { exit_status, time_stats },
60 exit_signal,
61 is_canonical: true,
62 })
63 }
64
65 pub fn to_wait_result(&self) -> WaitResult {
66 WaitResult {
67 pid: self.task.pid.clone(),
68 uid: self.task.real_creds().uid,
69 zombie_state: self.state.clone(),
70 exit_signal: self.exit_signal,
71 }
72 }
73
74 pub fn as_artificial(&self) -> Self {
75 ZombieProcess {
76 task: self.task.clone(),
77 state: self.state.clone(),
78 exit_signal: self.exit_signal,
79 is_canonical: false,
80 }
81 }
82
83 pub fn matches_selector(&self, selector: &ProcessSelector) -> bool {
84 match selector {
85 ProcessSelector::Any => true,
86 ProcessSelector::Pid(pid) => &self.task.pid == pid,
87 ProcessSelector::Pgid(pgid) => &self.task.thread_group().pgid() == pgid,
88 }
89 }
90
91 pub fn matches_selector_and_waiting_option(
92 &self,
93 selector: &ProcessSelector,
94 options: &WaitingOptions,
95 ) -> bool {
96 if !self.matches_selector(selector) {
97 return false;
98 }
99
100 if options.wait_for_all {
101 true
102 } else {
103 options.wait_for_clone == (self.exit_signal != Some(SIGCHLD))
106 }
107 }
108}
109
110pub trait ZombieReleaser {
116 fn remove_zombie(&mut self, pid: &Pid);
117}
118
119impl<'a> ZombieReleaser for PidTableGuard<'a> {
120 fn remove_zombie(&mut self, pid: &Pid) {
121 self.remove_zombie(pid);
122 }
123}
124
125impl Releasable for ZombieProcess {
126 type Context<'a> = &'a mut dyn ZombieReleaser;
127
128 fn release<'a>(self, pids: &'a mut dyn ZombieReleaser) {
129 if self.is_canonical {
130 pids.remove_zombie(&self.task.pid);
131 }
132 }
133}
134
135#[must_use = "Notifications must be explicitly delivered or discarded"]
153pub struct ZombieNotification {
154 pub recipient: Weak<ThreadGroup>,
156
157 pub zombie: OwnedRef<ZombieProcess>,
159}
160
161impl ZombieNotification {
162 pub fn new(recipient: Weak<ThreadGroup>, zombie: OwnedRef<ZombieProcess>) -> Self {
163 Self { recipient, zombie }
164 }
165
166 pub fn deliver(self, pids: &mut PidTableGuard<'_>) {
172 if let Some(parent) = self.recipient.upgrade() {
173 parent.do_zombie_notifications(self.zombie, pids);
174 } else {
175 log_warn!("Zombie {} reaped silently", self.zombie.task.get_pid());
176 self.zombie.release(pids);
177 }
178 }
179
180 pub fn discard(self, pids: &mut PidTableGuard<'_>) {
184 self.zombie.release(pids);
185 }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq)]
189pub struct WaitResult {
190 pub pid: Pid,
191 pub uid: uid_t,
192 pub zombie_state: ZombieState,
193 pub exit_signal: Option<Signal>,
194}
195
196impl WaitResult {
197 pub fn as_signal_info(&self) -> SignalInfo {
199 SignalInfo::with_detail(
200 SIGCHLD,
201 self.zombie_state.exit_status.signal_info_code(),
202 SignalDetail::SIGCHLD {
203 pid: self.pid.clone(),
204 uid: self.uid,
205 status: self.zombie_state.exit_status.signal_info_status(),
206 },
207 )
208 }
209}
210
211#[derive(Clone, Debug, PartialEq, Eq)]
213pub struct ZombieState {
214 pub exit_status: ExitStatus,
215 pub time_stats: TaskTimeStats,
216}