starnix_core/vfs/
pidfd.rs1use crate::task::{
6 CurrentTask, EventHandler, Pid, ProcessEntryRef, SignalHandler, SignalHandlerInner,
7 WaitCanceler, Waiter,
8};
9use crate::vfs::{
10 Anon, FileHandle, FileObject, FileOps, fileops_impl_dataless, fileops_impl_nonseekable,
11 fileops_impl_noop_sync,
12};
13use fuchsia_async as fasync;
14use starnix_uapi::errors::Errno;
15use starnix_uapi::open_flags::OpenFlags;
16use starnix_uapi::vfs::FdEvents;
17use starnix_uapi::{error, from_status_like_fdio};
18
19pub struct PidFdFileObject {
20 pid: Pid,
22
23 terminated_event: Option<zx::EventPair>,
30}
31
32impl PidFdFileObject {
33 pub(crate) fn pid(&self) -> &Pid {
35 &self.pid
36 }
37
38 fn get_signals_from_events(events: FdEvents) -> zx::Signals {
39 if events.contains(FdEvents::POLLIN) {
40 zx::Signals::EVENTPAIR_PEER_CLOSED
41 } else {
42 zx::Signals::NONE
43 }
44 }
45
46 fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
47 let mut events = FdEvents::empty();
48
49 if signals.contains(zx::Signals::EVENTPAIR_PEER_CLOSED) {
50 events |= FdEvents::POLLIN;
51 }
52
53 events
54 }
55}
56
57fn get_memory_manager_drop_event(pid: &Pid) -> Option<zx::EventPair> {
62 let Some(ProcessEntryRef::Process(proc)) = pid.get_process() else {
63 return None;
64 };
65 let task = pid.get_task().or_else(|_| proc.read().get_running_task());
66 task.ok().and_then(|task| task.mm().ok()).map(|mm| mm.drop_notifier.event())
67}
68
69async fn wait_for_process_release(
75 pid: Pid,
76 initial_mm_event: Option<zx::EventPair>,
77 zx_process: zx::Process,
78) {
79 let mut mm_event = initial_mm_event;
80 while let Some(event) = mm_event {
81 if fasync::OnSignals::new(&event, zx::Signals::EVENTPAIR_PEER_CLOSED).await.is_err() {
82 break;
83 }
84
85 mm_event = get_memory_manager_drop_event(&pid);
88 }
89
90 let _ = fasync::OnSignals::new(&zx_process, zx::Signals::PROCESS_TERMINATED).await;
91}
92
93async fn monitor_pidfd(
99 pid: Pid,
100 initial_mm_event: Option<zx::EventPair>,
101 zx_process: zx::Process,
102 local_event: zx::EventPair,
103) {
104 let pidfd_closed =
105 std::pin::pin!(fasync::OnSignals::new(&local_event, zx::Signals::EVENTPAIR_PEER_CLOSED));
106 let released = std::pin::pin!(wait_for_process_release(pid, initial_mm_event, zx_process));
107 let _ = futures::future::select(pidfd_closed, released).await;
108
109 }
112
113pub fn new_pidfd(
114 current_task: &CurrentTask,
115 pid: Pid,
116 flags: OpenFlags,
117) -> Result<FileHandle, Errno> {
118 let terminated_event = match pid.get_process() {
119 Some(ProcessEntryRef::Process(proc)) => {
120 let zx_process = proc
121 .process
122 .duplicate_handle(zx::Rights::SAME_RIGHTS)
123 .map_err(|status| from_status_like_fdio!(status))?;
124 let initial_mm_event = get_memory_manager_drop_event(&pid);
128 let (local_event, terminated_event) = zx::EventPair::create();
129 let monitored_pid = pid.clone();
130
131 current_task.kernel().kthreads.spawn_future(
132 move || monitor_pidfd(monitored_pid, initial_mm_event, zx_process, local_event),
133 "pidfd-monitor",
134 );
135
136 Some(terminated_event)
137 }
138 Some(ProcessEntryRef::Zombie) => None,
139 None => {
140 if pid.get_task().is_ok_and(|task| !task.is_leader()) {
141 return error!(EINVAL);
142 }
143 return error!(ESRCH);
144 }
145 };
146
147 Ok(Anon::new_private_file(
148 current_task,
149 Box::new(PidFdFileObject { pid, terminated_event }),
150 flags,
151 "[pidfd]",
152 ))
153}
154
155impl FileOps for PidFdFileObject {
156 fileops_impl_nonseekable!();
157 fileops_impl_dataless!();
158 fileops_impl_noop_sync!();
159
160 fn as_pid(&self, _file: &FileObject) -> Result<Pid, Errno> {
161 Ok(self.pid.clone())
162 }
163
164 fn wait_async(
165 &self,
166 _file: &FileObject,
167 _current_task: &CurrentTask,
168 waiter: &Waiter,
169 events: FdEvents,
170 handler: EventHandler,
171 ) -> Option<WaitCanceler> {
172 let terminated_event = self.terminated_event.as_ref()?;
173 let signal_handler = SignalHandler {
174 inner: SignalHandlerInner::ZxHandle(PidFdFileObject::get_events_from_signals),
175 event_handler: handler,
176 err_code: None,
177 };
178 let canceler = waiter
179 .wake_on_zircon_signals(
180 terminated_event,
181 PidFdFileObject::get_signals_from_events(events),
182 signal_handler,
183 )
184 .unwrap(); Some(WaitCanceler::new_port(canceler))
186 }
187
188 fn query_events(
189 &self,
190 _file: &FileObject,
191 _current_task: &CurrentTask,
192 ) -> Result<FdEvents, Errno> {
193 let Some(terminated_event) = &self.terminated_event else {
194 return Ok(FdEvents::POLLIN);
195 };
196 match terminated_event
197 .wait_one(zx::Signals::EVENTPAIR_PEER_CLOSED, zx::MonotonicInstant::ZERO)
198 .to_result()
199 {
200 Err(zx::Status::TIMED_OUT) => Ok(FdEvents::empty()),
201 Ok(zx::Signals::EVENTPAIR_PEER_CLOSED) => Ok(FdEvents::POLLIN),
202 result => unreachable!("unexpected result: {result:?}"),
203 }
204 }
205}