shadow_rs/host/descriptor/
timerfd.rs1use std::io::Write;
2use std::sync::{Arc, Weak};
3
4use atomic_refcell::AtomicRefCell;
5use linux_api::errno::Errno;
6use linux_api::ioctls::IoctlRequest;
7use linux_api::posix_types::kernel_off_t;
8use shadow_shim_helper_rs::{
9 emulated_time::EmulatedTime, simulation_time::SimulationTime, syscall_types::ForeignPtr,
10};
11
12use crate::cshadow as c;
13use crate::host::descriptor::listener::{StateEventSource, StateListenHandle, StateListenerFilter};
14use crate::host::descriptor::{FileMode, FileSignals, FileState, FileStatus};
15use crate::host::host::Host;
16use crate::host::memory_manager::MemoryManager;
17use crate::host::syscall::io::{IoVec, IoVecWriter};
18use crate::host::syscall::types::{SyscallError, SyscallResult};
19use crate::host::timer::Timer;
20use crate::utility::HostTreePointer;
21use crate::utility::callback_queue::CallbackQueue;
22
23pub struct TimerFd {
24 timer: Timer,
25 event_source: StateEventSource,
26 status: FileStatus,
27 state: FileState,
28 has_open_file: bool,
31}
32
33impl TimerFd {
34 pub fn new(status: FileStatus) -> Arc<AtomicRefCell<Self>> {
41 Arc::new_cyclic(|weak| {
44 let weak_cloned = weak.clone();
45 AtomicRefCell::new(Self {
46 timer: Timer::new(move |_host| Self::timer_expired(&weak_cloned)),
47 event_source: StateEventSource::new(),
48 state: FileState::ACTIVE,
49 status,
50 has_open_file: false,
51 })
52 })
53 }
54
55 fn timer_expired(timerfd_weak: &Weak<AtomicRefCell<TimerFd>>) {
57 let Some(timerfd) = timerfd_weak.upgrade() else {
58 log::trace!("Expired TimerFd no longer exists.");
59 return;
60 };
61
62 CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
66 timerfd.borrow_mut().refresh_state(cb_queue);
67 });
68 }
69
70 fn get_timer_count(&self) -> u64 {
72 self.timer.expiration_count()
73 }
74
75 pub fn get_timer_remaining(&self) -> Option<SimulationTime> {
78 self.timer.remaining_time()
79 }
80
81 pub fn get_timer_interval(&self) -> Option<SimulationTime> {
84 self.timer.expire_interval()
85 }
86
87 pub fn arm_timer(
90 &mut self,
91 host: &Host,
92 expire_time: EmulatedTime,
93 interval: Option<SimulationTime>,
94 cb_queue: &mut CallbackQueue,
95 ) {
96 self.timer.arm(host, expire_time, interval);
98 self.refresh_state(cb_queue);
99 }
100
101 pub fn disarm_timer(&mut self, cb_queue: &mut CallbackQueue) {
104 self.timer.disarm();
106 self.refresh_state(cb_queue);
107 }
108
109 pub fn status(&self) -> FileStatus {
110 self.status
111 }
112
113 pub fn set_status(&mut self, status: FileStatus) {
114 self.status = status;
115 }
116
117 pub fn mode(&self) -> FileMode {
118 FileMode::READ
119 }
120
121 pub fn has_open_file(&self) -> bool {
122 self.has_open_file
123 }
124
125 pub fn supports_sa_restart(&self) -> bool {
126 false
127 }
128
129 pub fn set_has_open_file(&mut self, val: bool) {
130 self.has_open_file = val;
131 }
132
133 pub fn readv(
134 &mut self,
135 iovs: &[IoVec],
136 offset: Option<kernel_off_t>,
137 _flags: std::ffi::c_int,
138 mem: &mut MemoryManager,
139 cb_queue: &mut CallbackQueue,
140 ) -> Result<isize, SyscallError> {
141 if offset.is_some() {
143 return Err(Errno::ESPIPE.into());
144 }
145
146 const NUM_BYTES: usize = 8;
149
150 let len: usize = iovs.iter().map(|x| x.len).sum();
151
152 if len < NUM_BYTES {
155 log::trace!("Reading from TimerFd requires a buffer of at least {NUM_BYTES} bytes",);
156 return Err(Errno::EINVAL.into());
157 }
158
159 let expiration_count = self.timer.consume_expiration_count();
160
161 if expiration_count == 0 {
162 log::trace!("TimerFd expiration count is 0 and cannot be read right now");
163 return Err(Errno::EWOULDBLOCK.into());
164 }
165
166 let mut writer = IoVecWriter::new(iovs, mem);
167 let to_write: [u8; NUM_BYTES] = expiration_count.to_ne_bytes();
168 writer.write_all(&to_write)?;
169
170 self.refresh_state(cb_queue);
172
173 Ok(NUM_BYTES.try_into().unwrap())
174 }
175
176 pub fn writev(
177 &mut self,
178 _iovs: &[IoVec],
179 _offset: Option<kernel_off_t>,
180 _flags: std::ffi::c_int,
181 _mem: &mut MemoryManager,
182 _cb_queue: &mut CallbackQueue,
183 ) -> Result<isize, SyscallError> {
184 Err(Errno::EINVAL.into())
186 }
187
188 pub fn close(&mut self, cb_queue: &mut CallbackQueue) -> Result<(), SyscallError> {
189 self.update_state(
191 FileState::CLOSED | FileState::ACTIVE | FileState::READABLE,
192 FileState::CLOSED,
193 FileSignals::empty(),
194 cb_queue,
195 );
196
197 Ok(())
198 }
199
200 pub fn ioctl(
201 &mut self,
202 request: IoctlRequest,
203 _arg_ptr: ForeignPtr<()>,
204 _memory_manager: &mut MemoryManager,
205 ) -> SyscallResult {
206 warn_once_then_debug!("We do not yet handle ioctl request {request:?} on TimerFds");
210 Err(Errno::EINVAL.into())
211 }
212
213 pub fn stat(&self) -> Result<linux_api::stat::stat, SyscallError> {
214 warn_once_then_debug!("We do not yet handle stat calls on timerfds");
215 Err(Errno::EINVAL.into())
216 }
217
218 pub fn add_listener(
219 &mut self,
220 monitoring_state: FileState,
221 monitoring_signals: FileSignals,
222 filter: StateListenerFilter,
223 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
224 + Send
225 + Sync
226 + 'static,
227 ) -> StateListenHandle {
228 self.event_source
229 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
230 }
231
232 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
233 self.event_source.add_legacy_listener(ptr);
234 }
235
236 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
237 self.event_source.remove_legacy_listener(ptr);
238 }
239
240 pub fn state(&self) -> FileState {
241 self.state
242 }
243
244 fn refresh_state(&mut self, cb_queue: &mut CallbackQueue) {
245 if self.state.contains(FileState::CLOSED) {
246 return;
247 }
248
249 let mut new_state = FileState::empty();
250
251 new_state.set(FileState::READABLE, self.get_timer_count() > 0);
253
254 self.update_state(
255 FileState::READABLE,
256 new_state,
257 FileSignals::empty(),
258 cb_queue,
259 );
260 }
261
262 fn update_state(
263 &mut self,
264 mask: FileState,
265 state: FileState,
266 signals: FileSignals,
267 cb_queue: &mut CallbackQueue,
268 ) {
269 let old_state = self.state;
270
271 self.state.remove(mask);
273 self.state.insert(state & mask);
274
275 self.handle_state_change(old_state, signals, cb_queue);
276 }
277
278 fn handle_state_change(
279 &mut self,
280 old_state: FileState,
281 signals: FileSignals,
282 cb_queue: &mut CallbackQueue,
283 ) {
284 let states_changed = self.state ^ old_state;
285
286 if states_changed.is_empty() && signals.is_empty() {
288 return;
289 }
290
291 self.event_source
292 .notify_listeners(self.state, states_changed, signals, cb_queue);
293 }
294}