vmm_sys_util/unix/
event.rs1use 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 pub struct EventFlag: u8 {
21 const NONBLOCK = 1 << 0;
23 const CLOEXEC = 1 << 1;
25 }
26}
27
28#[derive(Debug)]
42pub struct EventNotifier {
43 fd: File,
44}
45
46impl EventNotifier {
47 pub fn notify(&self) -> Result<(), io::Error> {
50 let v = 1u64;
51 (&self.fd).write_all(&v.to_ne_bytes())
52 }
53
54 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#[derive(Debug)]
96pub struct EventConsumer {
97 fd: File,
98}
99
100impl EventConsumer {
101 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 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 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 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 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 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#[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 let mut fds: [RawFd; 2] = [-1, -1];
186 let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
189 if ret < 0 {
190 return Err(io::Error::last_os_error());
191 }
192 let consumer = unsafe { EventConsumer::from_raw_fd(fds[0]) };
194 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(¬ifier.fd, libc::O_NONBLOCK)?;
199 }
200 if flags.contains(EventFlag::CLOEXEC) {
201 fcntl_setfd(&consumer.fd, libc::FD_CLOEXEC)?;
202 fcntl_setfd(¬ifier.fd, libc::FD_CLOEXEC)?;
203 }
204 Ok((consumer, notifier))
205}
206
207#[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 let consumer = unsafe { EventConsumer::from_raw_fd(eventfd.into_raw_fd()) };
237 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 let notifier = unsafe { EventNotifier::from_raw_fd(writer.into_raw_fd()) };
252 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 let notifier = unsafe { EventNotifier::from_raw_fd(writer.into_raw_fd()) };
264 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 let flags = unsafe { libc::fcntl(consumer.as_raw_fd(), libc::F_GETFD) };
296 assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
297 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 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 let flags = unsafe { libc::fcntl(notifier_cloned.as_raw_fd(), libc::F_GETFD) };
307 assert!(flags >= 0 && flags & libc::FD_CLOEXEC == 1);
308 }
309}