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 lseek(
219 &mut self,
220 _off: linux_api::posix_types::kernel_off_t,
221 _whence: linux_api::unistd::LSeekWhence,
222 ) -> Result<linux_api::posix_types::kernel_off_t, SyscallError> {
223 warn_once_then_debug!("We do not yet handle lseek calls on timerfd files");
224 Err(linux_api::errno::Errno::EBADF.into())
225 }
226
227 pub fn add_listener(
228 &mut self,
229 monitoring_state: FileState,
230 monitoring_signals: FileSignals,
231 filter: StateListenerFilter,
232 notify_fn: impl Fn(FileState, FileState, FileSignals, &mut CallbackQueue)
233 + Send
234 + Sync
235 + 'static,
236 ) -> StateListenHandle {
237 self.event_source
238 .add_listener(monitoring_state, monitoring_signals, filter, notify_fn)
239 }
240
241 pub fn add_legacy_listener(&mut self, ptr: HostTreePointer<c::StatusListener>) {
242 self.event_source.add_legacy_listener(ptr);
243 }
244
245 pub fn remove_legacy_listener(&mut self, ptr: *mut c::StatusListener) {
246 self.event_source.remove_legacy_listener(ptr);
247 }
248
249 pub fn state(&self) -> FileState {
250 self.state
251 }
252
253 fn refresh_state(&mut self, cb_queue: &mut CallbackQueue) {
254 if self.state.contains(FileState::CLOSED) {
255 return;
256 }
257
258 let mut new_state = FileState::empty();
259
260 new_state.set(FileState::READABLE, self.get_timer_count() > 0);
262
263 self.update_state(
264 FileState::READABLE,
265 new_state,
266 FileSignals::empty(),
267 cb_queue,
268 );
269 }
270
271 fn update_state(
272 &mut self,
273 mask: FileState,
274 state: FileState,
275 signals: FileSignals,
276 cb_queue: &mut CallbackQueue,
277 ) {
278 let old_state = self.state;
279
280 self.state.remove(mask);
282 self.state.insert(state & mask);
283
284 self.handle_state_change(old_state, signals, cb_queue);
285 }
286
287 fn handle_state_change(
288 &mut self,
289 old_state: FileState,
290 signals: FileSignals,
291 cb_queue: &mut CallbackQueue,
292 ) {
293 let states_changed = self.state ^ old_state;
294
295 if states_changed.is_empty() && signals.is_empty() {
297 return;
298 }
299
300 self.event_source
301 .notify_listeners(self.state, states_changed, signals, cb_queue);
302 }
303}