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 fn get_signals_from_events(events: FdEvents) -> zx::Signals {
34 if events.contains(FdEvents::POLLIN) {
35 zx::Signals::EVENTPAIR_PEER_CLOSED
36 } else {
37 zx::Signals::NONE
38 }
39 }
40
41 fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
42 let mut events = FdEvents::empty();
43
44 if signals.contains(zx::Signals::EVENTPAIR_PEER_CLOSED) {
45 events |= FdEvents::POLLIN;
46 }
47
48 events
49 }
50}
51
52fn get_memory_manager_drop_event(pid: &Pid) -> Option<zx::EventPair> {
57 let Some(ProcessEntryRef::Process(proc)) = pid.get_process() else {
58 return None;
59 };
60 let task = pid.get_task().or_else(|_| proc.read().get_running_task());
61 task.ok().and_then(|task| task.mm().ok()).map(|mm| mm.drop_notifier.event())
62}
63
64async fn wait_for_process_release(
70 pid: Pid,
71 initial_mm_event: Option<zx::EventPair>,
72 zx_process: zx::Process,
73) {
74 let mut mm_event = initial_mm_event;
75 while let Some(event) = mm_event {
76 if fasync::OnSignals::new(&event, zx::Signals::EVENTPAIR_PEER_CLOSED).await.is_err() {
77 break;
78 }
79
80 mm_event = get_memory_manager_drop_event(&pid);
83 }
84
85 let _ = fasync::OnSignals::new(&zx_process, zx::Signals::PROCESS_TERMINATED).await;
86}
87
88async fn monitor_pidfd(
94 pid: Pid,
95 initial_mm_event: Option<zx::EventPair>,
96 zx_process: zx::Process,
97 local_event: zx::EventPair,
98) {
99 let pidfd_closed =
100 std::pin::pin!(fasync::OnSignals::new(&local_event, zx::Signals::EVENTPAIR_PEER_CLOSED));
101 let released = std::pin::pin!(wait_for_process_release(pid, initial_mm_event, zx_process));
102 let _ = futures::future::select(pidfd_closed, released).await;
103
104 }
107
108pub fn new_pidfd(
109 current_task: &CurrentTask,
110 pid: Pid,
111 flags: OpenFlags,
112) -> Result<FileHandle, Errno> {
113 let terminated_event = match pid.get_process() {
114 Some(ProcessEntryRef::Process(proc)) => {
115 let zx_process = proc
116 .process
117 .duplicate_handle(zx::Rights::SAME_RIGHTS)
118 .map_err(|status| from_status_like_fdio!(status))?;
119 let initial_mm_event = get_memory_manager_drop_event(&pid);
123 let (local_event, terminated_event) = zx::EventPair::create();
124 let monitored_pid = pid.clone();
125
126 current_task.kernel().kthreads.spawn_future(
127 move || monitor_pidfd(monitored_pid, initial_mm_event, zx_process, local_event),
128 "pidfd-monitor",
129 );
130
131 Some(terminated_event)
132 }
133 Some(ProcessEntryRef::Zombie) => None,
134 None => {
135 if pid.get_task().is_ok() {
136 return error!(EINVAL);
137 }
138 return error!(ESRCH);
139 }
140 };
141
142 Ok(Anon::new_private_file(
143 current_task,
144 Box::new(PidFdFileObject { pid, terminated_event }),
145 flags,
146 "[pidfd]",
147 ))
148}
149
150impl FileOps for PidFdFileObject {
151 fileops_impl_nonseekable!();
152 fileops_impl_dataless!();
153 fileops_impl_noop_sync!();
154
155 fn as_pid(&self, _file: &FileObject) -> Result<Pid, Errno> {
156 Ok(self.pid.clone())
157 }
158
159 fn wait_async(
160 &self,
161 _file: &FileObject,
162 _current_task: &CurrentTask,
163 waiter: &Waiter,
164 events: FdEvents,
165 handler: EventHandler,
166 ) -> Option<WaitCanceler> {
167 let terminated_event = self.terminated_event.as_ref()?;
168 let signal_handler = SignalHandler {
169 inner: SignalHandlerInner::ZxHandle(PidFdFileObject::get_events_from_signals),
170 event_handler: handler,
171 err_code: None,
172 };
173 let canceler = waiter
174 .wake_on_zircon_signals(
175 terminated_event,
176 PidFdFileObject::get_signals_from_events(events),
177 signal_handler,
178 )
179 .unwrap(); Some(WaitCanceler::new_port(canceler))
181 }
182
183 fn query_events(
184 &self,
185 _file: &FileObject,
186 _current_task: &CurrentTask,
187 ) -> Result<FdEvents, Errno> {
188 let Some(terminated_event) = &self.terminated_event else {
189 return Ok(FdEvents::POLLIN);
190 };
191 match terminated_event
192 .wait_one(zx::Signals::EVENTPAIR_PEER_CLOSED, zx::MonotonicInstant::ZERO)
193 .to_result()
194 {
195 Err(zx::Status::TIMED_OUT) => Ok(FdEvents::empty()),
196 Ok(zx::Signals::EVENTPAIR_PEER_CLOSED) => Ok(FdEvents::POLLIN),
197 result => unreachable!("unexpected result: {result:?}"),
198 }
199 }
200}