Skip to main content

starnix_core/vfs/
pidfd.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::MemoryManager;
6use crate::task::{
7    CurrentTask, EventHandler, SignalHandler, SignalHandlerInner, ThreadGroup, ThreadGroupKey,
8    WaitCanceler, Waiter,
9};
10use crate::vfs::{
11    Anon, FileHandle, FileObject, FileOps, fileops_impl_dataless, fileops_impl_nonseekable,
12    fileops_impl_noop_sync,
13};
14use starnix_uapi::error;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::open_flags::OpenFlags;
17use starnix_uapi::vfs::FdEvents;
18
19pub struct PidFdFileObject {
20    /// The key of the task represented by this file.
21    tg: ThreadGroupKey,
22
23    // Receives a notification when the tracked process terminates.
24    terminated_event: zx::EventPair,
25}
26
27impl PidFdFileObject {
28    fn get_signals_from_events(events: FdEvents) -> zx::Signals {
29        if events.contains(FdEvents::POLLIN) {
30            zx::Signals::EVENTPAIR_PEER_CLOSED
31        } else {
32            zx::Signals::NONE
33        }
34    }
35
36    fn get_events_from_signals(signals: zx::Signals) -> FdEvents {
37        let mut events = FdEvents::empty();
38
39        if signals.contains(zx::Signals::EVENTPAIR_PEER_CLOSED) {
40            events |= FdEvents::POLLIN;
41        }
42
43        events
44    }
45}
46
47pub fn new_pidfd(
48    current_task: &CurrentTask,
49    proc: &ThreadGroup,
50    mm: &MemoryManager,
51    flags: OpenFlags,
52) -> FileHandle {
53    // We should really be monitoring the ThreadGroup's drop_notifier instead, but we also need to
54    // ensure that we're not signalling the pidfd until after all memory resources associated with
55    // the process are released. In the current Starnix codebase, there is a 1:1 correspondence
56    // between ThreadGroups (i.e. processes) and MemoryManagers, and the MemoryManager of a process
57    // may outlive the ThreadGroup in some circumstances. Therefore, as a temporary workaround, here
58    // we monitor the MemoryManager's drop_notifier, which is guaranteed to only fire when all the
59    // memory mappings associated with the process have been released. To be revisited once Starnix
60    // implements explicit cleanup of resources on process exit.
61    let terminated_event = mm.drop_notifier.event();
62
63    Anon::new_private_file(
64        current_task,
65        Box::new(PidFdFileObject { tg: proc.into(), terminated_event }),
66        flags,
67        "[pidfd]",
68    )
69}
70
71impl FileOps for PidFdFileObject {
72    fileops_impl_nonseekable!();
73    fileops_impl_dataless!();
74    fileops_impl_noop_sync!();
75
76    fn as_thread_group_key(&self, _file: &FileObject) -> Result<ThreadGroupKey, Errno> {
77        Ok(self.tg.clone())
78    }
79
80    fn wait_async(
81        &self,
82        _file: &FileObject,
83        _current_task: &CurrentTask,
84        waiter: &Waiter,
85        events: FdEvents,
86        handler: EventHandler,
87    ) -> Option<WaitCanceler> {
88        let signal_handler = SignalHandler {
89            inner: SignalHandlerInner::ZxHandle(PidFdFileObject::get_events_from_signals),
90            event_handler: handler,
91            err_code: None,
92        };
93        let canceler = waiter
94            .wake_on_zircon_signals(
95                &self.terminated_event,
96                PidFdFileObject::get_signals_from_events(events),
97                signal_handler,
98            )
99            .unwrap(); // errors cannot happen unless the kernel is out of memory
100        Some(WaitCanceler::new_port(canceler))
101    }
102
103    fn query_events(
104        &self,
105        _file: &FileObject,
106        _current_task: &CurrentTask,
107    ) -> Result<FdEvents, Errno> {
108        match self
109            .terminated_event
110            .wait_one(zx::Signals::EVENTPAIR_PEER_CLOSED, zx::MonotonicInstant::ZERO)
111            .to_result()
112        {
113            Err(zx::Status::TIMED_OUT) => Ok(FdEvents::empty()),
114            Ok(zx::Signals::EVENTPAIR_PEER_CLOSED) => Ok(FdEvents::POLLIN),
115            result => unreachable!("unexpected result: {result:?}"),
116        }
117    }
118}
119
120pub fn new_zombie_pidfd(current_task: &CurrentTask, flags: OpenFlags) -> FileHandle {
121    Anon::new_private_file(current_task, Box::new(ZombiePidFdFileObject {}), flags, "[pidfd]")
122}
123
124struct ZombiePidFdFileObject {}
125
126impl FileOps for ZombiePidFdFileObject {
127    fileops_impl_nonseekable!();
128    fileops_impl_dataless!();
129    fileops_impl_noop_sync!();
130
131    fn as_thread_group_key(&self, _file: &FileObject) -> Result<ThreadGroupKey, Errno> {
132        // There's nothing really reasonable to return here?
133        error!(EINVAL)
134    }
135
136    fn wait_async(
137        &self,
138        _file: &FileObject,
139        _current_task: &CurrentTask,
140        _waiter: &Waiter,
141        _events: FdEvents,
142        _handler: EventHandler,
143    ) -> Option<WaitCanceler> {
144        // There's nothing to wait on; is denying blocking sufficient?
145        None
146    }
147
148    fn query_events(
149        &self,
150        _file: &FileObject,
151        _current_task: &CurrentTask,
152    ) -> Result<FdEvents, Errno> {
153        Ok(FdEvents::POLLIN)
154    }
155}