1 | use std::{ |
2 | io, |
3 | os::unix::io::{ |
4 | AsFd, |
5 | AsRawFd, |
6 | BorrowedFd, |
7 | FromRawFd, |
8 | IntoRawFd, |
9 | OwnedFd, |
10 | RawFd, |
11 | }, |
12 | path::Path, |
13 | sync::{ |
14 | atomic::AtomicBool, |
15 | Arc, |
16 | } |
17 | }; |
18 | |
19 | use inotify_sys as ffi; |
20 | use libc::{ |
21 | F_GETFL, |
22 | F_SETFL, |
23 | O_NONBLOCK, |
24 | fcntl, |
25 | }; |
26 | |
27 | use crate::events::Events; |
28 | use crate::fd_guard::FdGuard; |
29 | use crate::util::read_into_buffer; |
30 | use crate::watches::{ |
31 | WatchDescriptor, |
32 | WatchMask, |
33 | Watches, |
34 | }; |
35 | |
36 | |
37 | #[cfg (feature = "stream" )] |
38 | use crate::stream::EventStream; |
39 | |
40 | |
41 | /// Idiomatic Rust wrapper around Linux's inotify API |
42 | /// |
43 | /// `Inotify` is a wrapper around an inotify instance. It generally tries to |
44 | /// adhere to the underlying inotify API closely, while making access to it |
45 | /// safe and convenient. |
46 | /// |
47 | /// Please refer to the [top-level documentation] for further details and a |
48 | /// usage example. |
49 | /// |
50 | /// [top-level documentation]: crate |
51 | #[derive (Debug)] |
52 | pub struct Inotify { |
53 | fd: Arc<FdGuard>, |
54 | } |
55 | |
56 | impl Inotify { |
57 | /// Creates an [`Inotify`] instance |
58 | /// |
59 | /// Initializes an inotify instance by calling [`inotify_init1`]. |
60 | /// |
61 | /// This method passes both flags accepted by [`inotify_init1`], not giving |
62 | /// the user any choice in the matter, as not passing the flags would be |
63 | /// inappropriate in the context of this wrapper: |
64 | /// |
65 | /// - [`IN_CLOEXEC`] prevents leaking file descriptors to other processes. |
66 | /// - [`IN_NONBLOCK`] controls the blocking behavior of the inotify API, |
67 | /// which is entirely managed by this wrapper. |
68 | /// |
69 | /// # Errors |
70 | /// |
71 | /// Directly returns the error from the call to [`inotify_init1`], without |
72 | /// adding any error conditions of its own. |
73 | /// |
74 | /// # Examples |
75 | /// |
76 | /// ``` |
77 | /// use inotify::Inotify; |
78 | /// |
79 | /// let inotify = Inotify::init() |
80 | /// .expect("Failed to initialize an inotify instance" ); |
81 | /// ``` |
82 | /// |
83 | /// [`inotify_init1`]: inotify_sys::inotify_init1 |
84 | /// [`IN_CLOEXEC`]: inotify_sys::IN_CLOEXEC |
85 | /// [`IN_NONBLOCK`]: inotify_sys::IN_NONBLOCK |
86 | pub fn init() -> io::Result<Inotify> { |
87 | let fd = unsafe { |
88 | // Initialize inotify and pass both `IN_CLOEXEC` and `IN_NONBLOCK`. |
89 | // |
90 | // `IN_NONBLOCK` is needed, because `Inotify` manages blocking |
91 | // behavior for the API consumer, and the way we do that is to make |
92 | // everything non-blocking by default and later override that as |
93 | // required. |
94 | // |
95 | // Passing `IN_CLOEXEC` prevents leaking file descriptors to |
96 | // processes executed by this process and seems to be a best |
97 | // practice. I don't grasp this issue completely and failed to find |
98 | // any authoritative sources on the topic. There's some discussion in |
99 | // the open(2) and fcntl(2) man pages, but I didn't find that |
100 | // helpful in understanding the issue of leaked file descriptors. |
101 | // For what it's worth, there's a Rust issue about this: |
102 | // https://github.com/rust-lang/rust/issues/12148 |
103 | ffi::inotify_init1(ffi::IN_CLOEXEC | ffi::IN_NONBLOCK) |
104 | }; |
105 | |
106 | if fd == -1 { |
107 | return Err(io::Error::last_os_error()); |
108 | } |
109 | |
110 | Ok(Inotify { |
111 | fd: Arc::new(FdGuard { |
112 | fd, |
113 | close_on_drop: AtomicBool::new(true), |
114 | }), |
115 | }) |
116 | } |
117 | |
118 | /// Gets an interface that allows adding and removing watches. |
119 | /// See [`Watches::add`] and [`Watches::remove`]. |
120 | pub fn watches(&self) -> Watches { |
121 | Watches::new(self.fd.clone()) |
122 | } |
123 | |
124 | /// Deprecated: use `Inotify.watches().add()` instead |
125 | #[deprecated = "use `Inotify.watches().add()` instead" ] |
126 | pub fn add_watch<P>(&mut self, path: P, mask: WatchMask) |
127 | -> io::Result<WatchDescriptor> |
128 | where P: AsRef<Path> |
129 | { |
130 | self.watches().add(path, mask) |
131 | } |
132 | |
133 | /// Deprecated: use `Inotify.watches().remove()` instead |
134 | #[deprecated = "use `Inotify.watches().remove()` instead" ] |
135 | pub fn rm_watch(&mut self, wd: WatchDescriptor) -> io::Result<()> { |
136 | self.watches().remove(wd) |
137 | } |
138 | |
139 | /// Waits until events are available, then returns them |
140 | /// |
141 | /// Blocks the current thread until at least one event is available. If this |
142 | /// is not desirable, please consider [`Inotify::read_events`]. |
143 | /// |
144 | /// This method calls [`Inotify::read_events`] internally and behaves |
145 | /// essentially the same, apart from the blocking behavior. Please refer to |
146 | /// the documentation of [`Inotify::read_events`] for more information. |
147 | pub fn read_events_blocking<'a>(&mut self, buffer: &'a mut [u8]) |
148 | -> io::Result<Events<'a>> |
149 | { |
150 | unsafe { |
151 | let res = fcntl(**self.fd, F_GETFL); |
152 | if res == -1 { |
153 | return Err(io::Error::last_os_error()); |
154 | } |
155 | if fcntl(**self.fd, F_SETFL, res & !O_NONBLOCK) == -1 { |
156 | return Err(io::Error::last_os_error()); |
157 | } |
158 | }; |
159 | let result = self.read_events(buffer); |
160 | unsafe { |
161 | let res = fcntl(**self.fd, F_GETFL); |
162 | if res == -1 { |
163 | return Err(io::Error::last_os_error()); |
164 | } |
165 | if fcntl(**self.fd, F_SETFL, res | O_NONBLOCK) == -1 { |
166 | return Err(io::Error::last_os_error()); |
167 | } |
168 | }; |
169 | |
170 | result |
171 | } |
172 | |
173 | /// Returns one buffer's worth of available events |
174 | /// |
175 | /// Reads as many events as possible into `buffer`, and returns an iterator |
176 | /// over them. If no events are available, an iterator is still returned. If |
177 | /// you need a method that will block until at least one event is available, |
178 | /// please consider [`read_events_blocking`]. |
179 | /// |
180 | /// Please note that inotify will merge identical successive unread events |
181 | /// into a single event. This means this method can not be used to count the |
182 | /// number of file system events. |
183 | /// |
184 | /// The `buffer` argument, as the name indicates, is used as a buffer for |
185 | /// the inotify events. Its contents may be overwritten. |
186 | /// |
187 | /// # Errors |
188 | /// |
189 | /// This function directly returns all errors from the call to [`read`]. |
190 | /// In addition, [`ErrorKind::UnexpectedEof`] is returned, if the call to |
191 | /// [`read`] returns `0`, signaling end-of-file. |
192 | /// |
193 | /// If `buffer` is too small, this will result in an error with |
194 | /// [`ErrorKind::InvalidInput`]. On very old Linux kernels, |
195 | /// [`ErrorKind::UnexpectedEof`] will be returned instead. |
196 | /// |
197 | /// # Examples |
198 | /// |
199 | /// ```no_run |
200 | /// use inotify::Inotify; |
201 | /// use std::io::ErrorKind; |
202 | /// |
203 | /// let mut inotify = Inotify::init() |
204 | /// .expect("Failed to initialize an inotify instance" ); |
205 | /// |
206 | /// let mut buffer = [0; 1024]; |
207 | /// let events = loop { |
208 | /// match inotify.read_events(&mut buffer) { |
209 | /// Ok(events) => break events, |
210 | /// Err(error) if error.kind() == ErrorKind::WouldBlock => continue, |
211 | /// _ => panic!("Error while reading events" ), |
212 | /// } |
213 | /// }; |
214 | /// |
215 | /// for event in events { |
216 | /// // Handle event |
217 | /// } |
218 | /// ``` |
219 | /// |
220 | /// [`read_events_blocking`]: Self::read_events_blocking |
221 | /// [`read`]: libc::read |
222 | /// [`ErrorKind::UnexpectedEof`]: std::io::ErrorKind::UnexpectedEof |
223 | /// [`ErrorKind::InvalidInput`]: std::io::ErrorKind::InvalidInput |
224 | pub fn read_events<'a>(&mut self, buffer: &'a mut [u8]) |
225 | -> io::Result<Events<'a>> |
226 | { |
227 | let num_bytes = read_into_buffer(**self.fd, buffer); |
228 | |
229 | let num_bytes = match num_bytes { |
230 | 0 => { |
231 | return Err( |
232 | io::Error::new( |
233 | io::ErrorKind::UnexpectedEof, |
234 | "`read` return `0`, signaling end-of-file" |
235 | ) |
236 | ); |
237 | } |
238 | -1 => { |
239 | let error = io::Error::last_os_error(); |
240 | return Err(error); |
241 | }, |
242 | _ if num_bytes < 0 => { |
243 | panic!(" {} {} {} {} {} {}" , |
244 | "Unexpected return value from `read`. Received a negative" , |
245 | "value that was not `-1`. According to the `read` man page" , |
246 | "this shouldn't happen, as either `-1` is returned on" , |
247 | "error, `0` on end-of-file, or a positive value for the" , |
248 | "number of bytes read. Returned value:" , |
249 | num_bytes, |
250 | ); |
251 | } |
252 | _ => { |
253 | // The value returned by `read` should be `isize`. Let's quickly |
254 | // verify this with the following assignment, so we can be sure |
255 | // our cast below is valid. |
256 | let num_bytes: isize = num_bytes; |
257 | |
258 | // The type returned by `read` is `isize`, and we've ruled out |
259 | // all negative values with the match arms above. This means we |
260 | // can safely cast to `usize`. |
261 | debug_assert!(num_bytes > 0); |
262 | num_bytes as usize |
263 | } |
264 | }; |
265 | |
266 | Ok(Events::new(Arc::downgrade(&self.fd), buffer, num_bytes)) |
267 | } |
268 | |
269 | /// Deprecated: use `into_event_stream()` instead, which enforces a single `Stream` and predictable reads. |
270 | /// Using this method to create multiple `EventStream` instances from one `Inotify` is unsupported, |
271 | /// as they will contend over one event source and each produce unpredictable stream contents. |
272 | #[deprecated = "use `into_event_stream()` instead, which enforces a single Stream and predictable reads" ] |
273 | #[cfg (feature = "stream" )] |
274 | pub fn event_stream<T>(&mut self, buffer: T) |
275 | -> io::Result<EventStream<T>> |
276 | where |
277 | T: AsMut<[u8]> + AsRef<[u8]>, |
278 | { |
279 | EventStream::new(self.fd.clone(), buffer) |
280 | } |
281 | |
282 | /// Create a stream which collects events. Consumes the `Inotify` instance. |
283 | /// |
284 | /// Returns a `Stream` over all events that are available. This stream is an |
285 | /// infinite source of events. |
286 | /// |
287 | /// An internal buffer which can hold the largest possible event is used. |
288 | #[cfg (feature = "stream" )] |
289 | pub fn into_event_stream<T>(self, buffer: T) |
290 | -> io::Result<EventStream<T>> |
291 | where |
292 | T: AsMut<[u8]> + AsRef<[u8]>, |
293 | { |
294 | EventStream::new(self.fd, buffer) |
295 | } |
296 | |
297 | /// Creates an `Inotify` instance using the file descriptor which was originally |
298 | /// initialized in `Inotify::init`. This is intended to be used to transform an |
299 | /// `EventStream` back into an `Inotify`. Do not attempt to clone `Inotify` with this. |
300 | #[cfg (feature = "stream" )] |
301 | pub(crate) fn from_file_descriptor(fd: Arc<FdGuard>) -> Self |
302 | { |
303 | Inotify { |
304 | fd, |
305 | } |
306 | } |
307 | |
308 | /// Closes the inotify instance |
309 | /// |
310 | /// Closes the file descriptor referring to the inotify instance. The user |
311 | /// usually doesn't have to call this function, as the underlying inotify |
312 | /// instance is closed automatically, when [`Inotify`] is dropped. |
313 | /// |
314 | /// # Errors |
315 | /// |
316 | /// Directly returns the error from the call to [`close`], without adding any |
317 | /// error conditions of its own. |
318 | /// |
319 | /// # Examples |
320 | /// |
321 | /// ``` |
322 | /// use inotify::Inotify; |
323 | /// |
324 | /// let mut inotify = Inotify::init() |
325 | /// .expect("Failed to initialize an inotify instance" ); |
326 | /// |
327 | /// inotify.close() |
328 | /// .expect("Failed to close inotify instance" ); |
329 | /// ``` |
330 | /// |
331 | /// [`close`]: libc::close |
332 | pub fn close(self) -> io::Result<()> { |
333 | // `self` will be dropped when this method returns. If this is the only |
334 | // owner of `fd`, the `Arc` will also be dropped. The `Drop` |
335 | // implementation for `FdGuard` will attempt to close the file descriptor |
336 | // again, unless this flag here is cleared. |
337 | self.fd.should_not_close(); |
338 | |
339 | match unsafe { ffi::close(**self.fd) } { |
340 | 0 => Ok(()), |
341 | _ => Err(io::Error::last_os_error()), |
342 | } |
343 | } |
344 | } |
345 | |
346 | impl AsRawFd for Inotify { |
347 | #[inline ] |
348 | fn as_raw_fd(&self) -> RawFd { |
349 | self.fd.as_raw_fd() |
350 | } |
351 | } |
352 | |
353 | impl FromRawFd for Inotify { |
354 | unsafe fn from_raw_fd(fd: RawFd) -> Self { |
355 | Inotify { |
356 | fd: Arc::new(data:FdGuard::from_raw_fd(fd)) |
357 | } |
358 | } |
359 | } |
360 | |
361 | impl IntoRawFd for Inotify { |
362 | #[inline ] |
363 | fn into_raw_fd(self) -> RawFd { |
364 | self.fd.should_not_close(); |
365 | self.fd.fd |
366 | } |
367 | } |
368 | |
369 | impl AsFd for Inotify { |
370 | #[inline ] |
371 | fn as_fd(&self) -> BorrowedFd<'_> { |
372 | self.fd.as_fd() |
373 | } |
374 | } |
375 | |
376 | impl From<Inotify> for OwnedFd { |
377 | fn from(fd: Inotify) -> OwnedFd { |
378 | unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) } |
379 | } |
380 | } |
381 | |
382 | impl From<OwnedFd> for Inotify { |
383 | fn from(fd: OwnedFd) -> Inotify { |
384 | unsafe { Inotify::from_raw_fd(fd.into_raw_fd()) } |
385 | } |
386 | } |
387 | |