vmm_sys_util/unix/
event.rs

1// SPDX-License-Identifier: BSD-3-Clause
2//! This module provides a platform-independent interface for event notification,
3//! enabling support across multiple operating systems.
4//!
5//! Internally, it uses a file descriptor that can be backed by various mechanisms
6//! such as `eventfd`, `pipe`, or other OS-specific primitives.
7
8use std::io::{Read, Write};
9use std::os::fd::RawFd;
10use std::{
11    fs::File,
12    io,
13    os::fd::{AsRawFd, FromRawFd, IntoRawFd},
14    result::Result,
15};
16
17bitflags::bitflags! {
18    /// EventFlag
19    /// This enum is used to define flags for the event notifier and consumer.
20    pub struct EventFlag: u8 {
21        /// Non-blocking flag
22        const NONBLOCK = 1 << 0;
23        /// Close-on-exec flag
24        const CLOEXEC = 1 << 1;
25    }
26}
27
28/// EventNotifier
29/// This is a generic event notifier that can be used with eventfd or pipefd.
30/// It allows writing a value to the file descriptor to notify an event.
31///
32/// # Examples
33///
34/// ```
35/// use std::os::fd::FromRawFd;
36/// use std::os::unix::io::IntoRawFd;
37/// use vmm_sys_util::event::EventNotifier;
38/// let (_, writer) = std::io::pipe().expect("Failed to create pipe");
39/// let notifier = unsafe { EventNotifier::from_raw_fd(writer.into_raw_fd()) };
40/// ```
41#[derive(Debug)]
42pub struct EventNotifier {
43    fd: File,
44}
45
46impl EventNotifier {
47    /// Write a value to the EventNotifier's fd
48    /// Writing 1 to fd is for compatibility with Eventfd
49    pub fn notify(&self) -> Result<(), io::Error> {
50        let v = 1u64;
51        (&self.fd).write_all(&v.to_ne_bytes())
52    }
53
54    /// Clone this EventNotifier.
55    pub fn try_clone(&self) -> Result<EventNotifier, io::Error> {
56        Ok(EventNotifier {
57            fd: self.fd.try_clone()?,
58        })
59    }
60}
61
62impl AsRawFd for EventNotifier {
63    fn as_raw_fd(&self) -> RawFd {
64        self.fd.as_raw_fd()
65    }
66}
67
68impl FromRawFd for EventNotifier {
69    unsafe fn from_raw_fd(fd: RawFd) -> Self {
70        EventNotifier {
71            fd: File::from_raw_fd(fd),
72        }
73    }
74}
75
76impl IntoRawFd for EventNotifier {
77    fn into_raw_fd(self) -> RawFd {
78        self.fd.into_raw_fd()
79    }
80}
81
82/// EventConsumer
83/// This is a generic event consumer that can be used with eventfd or pipefd.
84/// It allows reading a value from the file descriptor to consume an event.
85///
86/// # Examples
87///
88/// ```
89/// use std::os::fd::FromRawFd;
90/// use std::os::unix::io::IntoRawFd;
91/// use vmm_sys_util::event::EventConsumer;
92/// let (reader, _) = std::io::pipe().expect("Failed to create pipe");
93/// let consumer = unsafe { EventConsumer::from_raw_fd(reader.into_raw_fd()) };
94/// ```
95#[derive(Debug)]
96pub struct EventConsumer {
97    fd: File,
98}
99
100impl EventConsumer {
101    /// Read a value from the EventConsumer.
102    pub fn consume(&self) -> Result<(), io::Error> {
103        let mut buf = [0u8; size_of::<u64>()];
104        (&self.fd).read_exact(buf.as_mut_slice()).map(|_| Ok(()))?
105    }
106
107    /// Clone this EventConsumer.
108    pub fn try_clone(&self) -> Result<EventConsumer, io::Error> {
109        Ok(EventConsumer {
110            fd: self.fd.try_clone()?,
111        })
112    }
113}
114
115impl AsRawFd for EventConsumer {
116    fn as_raw_fd(&self) -> RawFd {
117        self.fd.as_raw_fd()
118    }
119}
120
121impl FromRawFd for EventConsumer {
122    unsafe fn from_raw_fd(fd: RawFd) -> Self {
123        EventConsumer {
124            fd: File::from_raw_fd(fd),
125        }
126    }
127}
128
129impl IntoRawFd for EventConsumer {
130    fn into_raw_fd(self) -> RawFd {
131        self.fd.into_raw_fd()
132    }
133}
134
135#[cfg(not(any(target_os = "linux", target_os = "android")))]
136fn fcntl_setfl(file: &File, flag: i32) -> Result<(), io::Error> {
137    // SAFETY: Rust's I/O safety ensures `file` contains a valid FD.
138    let flags = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL) };
139    if flags < 0 {
140        return Err(io::Error::last_os_error());
141    }
142    // SAFETY: Rust's I/O safety ensures `file` contains a valid FD.
143    let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFL, flags | flag) };
144    if ret < 0 {
145        return Err(io::Error::last_os_error());
146    }
147    Ok(())
148}
149
150#[cfg(not(any(target_os = "linux", target_os = "android")))]
151fn fcntl_setfd(file: &File, flag: i32) -> Result<(), io::Error> {
152    // SAFETY: Rust's I/O safety ensures `file` contains a valid FD.
153    let flags = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFD) };
154    if flags < 0 {
155        return Err(io::Error::last_os_error());
156    }
157    // SAFETY: Rust's I/O safety ensures `file` contains a valid FD.
158    let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETFD, flags | flag) };
159    if ret < 0 {
160        return Err(io::Error::last_os_error());
161    }
162    Ok(())
163}
164
165/// Create a new EventNotifier and EventConsumer using a pipe.
166///
167/// # Arguments
168///
169/// * `flags` - Flags to set on the file descriptor, such as `EventFlag::NONBLOCK` or `EventFlag::CLOEXEC`.
170///
171/// # Examples
172///
173/// ```
174/// use vmm_sys_util::event::{new_event_consumer_and_notifier, EventFlag};
175/// let (consumer, notifier) = new_event_consumer_and_notifier(EventFlag::NONBLOCK)
176///     .expect("Failed to create notifier and consumer");
177/// notifier.notify().unwrap();
178/// assert!(consumer.consume().is_ok());
179/// ```
180#[cfg(not(any(target_os = "linux", target_os = "android")))]
181pub fn new_event_consumer_and_notifier(
182    flags: EventFlag,
183) -> Result<(EventConsumer, EventNotifier), io::Error> {
184    // Use a pipe for non-Linux platforms.
185    let mut fds: [RawFd; 2] = [-1, -1];
186    // SAFETY: This is safe because pipe merely allocated a read fd and a write fd
187    // for our process and we handle the error case.
188    let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
189    if ret < 0 {
190        return Err(io::Error::last_os_error());
191    }
192    // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
193    let consumer = unsafe { EventConsumer::from_raw_fd(fds[0]) };
194    // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
195    let notifier = unsafe { EventNotifier::from_raw_fd(fds[1]) };
196    if flags.contains(EventFlag::NONBLOCK) {
197        fcntl_setfl(&consumer.fd, libc::O_NONBLOCK)?;
198        fcntl_setfl(&notifier.fd, libc::O_NONBLOCK)?;
199    }
200    if flags.contains(EventFlag::CLOEXEC) {
201        fcntl_setfd(&consumer.fd, libc::FD_CLOEXEC)?;
202        fcntl_setfd(&notifier.fd, libc::FD_CLOEXEC)?;
203    }
204    Ok((consumer, notifier))
205}
206
207/// Create a new EventNotifier and EventConsumer using eventfd.
208///
209/// # Arguments
210///
211/// * `flags` - Flags to set on the file descriptor, such as `EventFlag::NONBLOCK` or `EventFlag::CLOEXEC`.
212///
213/// # Examples
214///
215/// ```
216/// use vmm_sys_util::event::{new_event_consumer_and_notifier, EventFlag};
217/// let (consumer, notifier) = new_event_consumer_and_notifier(EventFlag::NONBLOCK)
218///     .expect("Failed to create consumer and notifier");
219/// notifier.notify().unwrap();
220/// assert!(consumer.consume().is_ok());
221/// ```
222#[cfg(any(target_os = "linux", target_os = "android"))]
223pub fn new_event_consumer_and_notifier(
224    flags: EventFlag,
225) -> Result<(EventConsumer, EventNotifier), io::Error> {
226    let mut efd_flags = 0;
227    if flags.contains(EventFlag::NONBLOCK) {
228        efd_flags |= libc::EFD_NONBLOCK;
229    }
230    if flags.contains(EventFlag::CLOEXEC) {
231        efd_flags |= libc::EFD_CLOEXEC;
232    }
233    let eventfd = crate::linux::eventfd::EventFd::new(efd_flags)?;
234    let eventfd_clone = eventfd.try_clone()?;
235    // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
236    let consumer = unsafe { EventConsumer::from_raw_fd(eventfd.into_raw_fd()) };
237    // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
238    let notifier = unsafe { EventNotifier::from_raw_fd(eventfd_clone.into_raw_fd()) };
239    Ok((consumer, notifier))
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use std::{io::pipe, os::fd::IntoRawFd};
246
247    #[test]
248    fn test_notify_and_consume() {
249        let (reader, writer) = pipe().expect("Failed to create pipe");
250        // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
251        let notifier = unsafe { EventNotifier::from_raw_fd(writer.into_raw_fd()) };
252        // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
253        let consumer = unsafe { EventConsumer::from_raw_fd(reader.into_raw_fd()) };
254
255        notifier.notify().unwrap();
256        assert!(consumer.consume().is_ok());
257    }
258
259    #[test]
260    fn test_clone() {
261        let (reader, writer) = pipe().expect("Failed to create pipe");
262        // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
263        let notifier = unsafe { EventNotifier::from_raw_fd(writer.into_raw_fd()) };
264        // SAFETY: Safe because we check the fd is valid. And the kernel gave us an fd that we own.
265        let consumer = unsafe { EventConsumer::from_raw_fd(reader.into_raw_fd()) };
266
267        let cloned_notifier = notifier.try_clone().expect("Failed to clone notifier");
268        let cloned_consumer = consumer.try_clone().expect("Failed to clone consumer");
269
270        cloned_notifier.notify().unwrap();
271        assert!(cloned_consumer.consume().is_ok());
272    }
273
274    #[test]
275    fn test_new_event_notifier_and_consumer() {
276        let (consumer, notifier) = new_event_consumer_and_notifier(EventFlag::empty())
277            .expect("Failed to create notifier and consumer");
278        notifier.notify().unwrap();
279        assert!(consumer.consume().is_ok());
280    }
281
282    #[test]
283    fn test_nonblock() {
284        let (consumer, _notifier) = new_event_consumer_and_notifier(EventFlag::NONBLOCK)
285            .expect("Failed to create notifier and consumer");
286        let err = consumer.consume().unwrap_err();
287        assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
288    }
289
290    #[test]
291    fn test_cloexec() {
292        let (consumer, notifier) = new_event_consumer_and_notifier(EventFlag::CLOEXEC)
293            .expect("Failed to create notifier and consumer");
294        // SAFETY: This is safe because we check the fd and the return value are valid.
295        let flags = unsafe { libc::fcntl(consumer.as_raw_fd(), libc::F_GETFD) };
296        assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
297        // SAFETY: This is safe because we check the fd and the return value are valid.
298        let flags = unsafe { libc::fcntl(notifier.as_raw_fd(), libc::F_GETFD) };
299        assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
300        let consumer_cloned = consumer.try_clone().expect("Failed to clone consumer");
301        // SAFETY: This is safe because we check the fd and the return value are valid.
302        let flags = unsafe { libc::fcntl(consumer_cloned.as_raw_fd(), libc::F_GETFD) };
303        assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
304        let notifier_cloned = notifier.try_clone().expect("Failed to clone notifier");
305        // SAFETY: This is safe because we check the fd and the return value are valid.
306        let flags = unsafe { libc::fcntl(notifier_cloned.as_raw_fd(), libc::F_GETFD) };
307        assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
308    }
309}