1 | use std::{ |
2 | ffi::CString, |
3 | io, |
4 | os::unix::ffi::OsStrExt, |
5 | os::unix::io::{ |
6 | AsRawFd, |
7 | FromRawFd, |
8 | IntoRawFd, |
9 | RawFd, |
10 | }, |
11 | path::Path, |
12 | sync::{ |
13 | atomic::AtomicBool, |
14 | Arc, |
15 | } |
16 | }; |
17 | |
18 | use inotify_sys as ffi; |
19 | use libc::{ |
20 | F_GETFL, |
21 | F_SETFD, |
22 | F_SETFL, |
23 | FD_CLOEXEC, |
24 | O_NONBLOCK, |
25 | fcntl, |
26 | }; |
27 | |
28 | use crate::events::Events; |
29 | use crate::fd_guard::FdGuard; |
30 | use crate::util::read_into_buffer; |
31 | use crate::watches::{ |
32 | WatchDescriptor, |
33 | WatchMask, |
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]: index.html |
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`]: struct.Inotify.html |
84 | /// [`inotify_init1`]: ../inotify_sys/fn.inotify_init1.html |
85 | /// [`IN_CLOEXEC`]: ../inotify_sys/constant.IN_CLOEXEC.html |
86 | /// [`IN_NONBLOCK`]: ../inotify_sys/constant.IN_NONBLOCK.html |
87 | pub fn init() -> io::Result<Inotify> { |
88 | // Initialize inotify and set CLOEXEC and NONBLOCK flags. |
89 | // |
90 | // NONBLOCK is needed, because `Inotify` manages blocking behavior for |
91 | // the API consumer, and the way we do that is to make everything non- |
92 | // blocking by default and later override that as required. |
93 | // |
94 | // CLOEXEC prevents leaking file descriptors to processes executed by |
95 | // this process and seems to be a best practice. I don't grasp this |
96 | // issue completely and failed to find any authoritative sources on the |
97 | // topic. There's some discussion in the open(2) and fcntl(2) man pages, |
98 | // but I didn't find that helpful in understanding the issue of leaked |
99 | // file descriptors. For what it's worth, there's a Rust issue about |
100 | // this: |
101 | // https://github.com/rust-lang/rust/issues/12148 |
102 | let fd = unsafe { |
103 | let fd = ffi::inotify_init(); |
104 | if fd == -1 { |
105 | return Err(io::Error::last_os_error()); |
106 | } |
107 | if fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 { |
108 | return Err(io::Error::last_os_error()); |
109 | } |
110 | if fcntl(fd, F_SETFL, O_NONBLOCK) == -1 { |
111 | return Err(io::Error::last_os_error()); |
112 | } |
113 | fd |
114 | }; |
115 | |
116 | Ok(Inotify { |
117 | fd: Arc::new(FdGuard { |
118 | fd, |
119 | close_on_drop: AtomicBool::new(true), |
120 | }), |
121 | }) |
122 | } |
123 | |
124 | /// Adds or updates a watch for the given path |
125 | /// |
126 | /// Adds a new watch or updates an existing one for the file referred to by |
127 | /// `path`. Returns a watch descriptor that can be used to refer to this |
128 | /// watch later. |
129 | /// |
130 | /// The `mask` argument defines what kind of changes the file should be |
131 | /// watched for, and how to do that. See the documentation of [`WatchMask`] |
132 | /// for details. |
133 | /// |
134 | /// If this method is used to add a new watch, a new [`WatchDescriptor`] is |
135 | /// returned. If it is used to update an existing watch, a |
136 | /// [`WatchDescriptor`] that equals the previously returned |
137 | /// [`WatchDescriptor`] for that watch is returned instead. |
138 | /// |
139 | /// Under the hood, this method just calls [`inotify_add_watch`] and does |
140 | /// some trivial translation between the types on the Rust side and the C |
141 | /// side. |
142 | /// |
143 | /// # Attention: Updating watches and hardlinks |
144 | /// |
145 | /// As mentioned above, this method can be used to update an existing watch. |
146 | /// This is usually done by calling this method with the same `path` |
147 | /// argument that it has been called with before. But less obviously, it can |
148 | /// also happen if the method is called with a different path that happens |
149 | /// to link to the same inode. |
150 | /// |
151 | /// You can detect this by keeping track of [`WatchDescriptor`]s and the |
152 | /// paths they have been returned for. If the same [`WatchDescriptor`] is |
153 | /// returned for a different path (and you haven't freed the |
154 | /// [`WatchDescriptor`] by removing the watch), you know you have two paths |
155 | /// pointing to the same inode, being watched by the same watch. |
156 | /// |
157 | /// # Errors |
158 | /// |
159 | /// Directly returns the error from the call to |
160 | /// [`inotify_add_watch`][`inotify_add_watch`] (translated into an |
161 | /// `io::Error`), without adding any error conditions of |
162 | /// its own. |
163 | /// |
164 | /// # Examples |
165 | /// |
166 | /// ``` |
167 | /// use inotify::{ |
168 | /// Inotify, |
169 | /// WatchMask, |
170 | /// }; |
171 | /// |
172 | /// let mut inotify = Inotify::init() |
173 | /// .expect("Failed to initialize an inotify instance" ); |
174 | /// |
175 | /// # // Create a temporary file, so `add_watch` won't return an error. |
176 | /// # use std::fs::File; |
177 | /// # File::create("/tmp/inotify-rs-test-file" ) |
178 | /// # .expect("Failed to create test file" ); |
179 | /// # |
180 | /// inotify.add_watch("/tmp/inotify-rs-test-file" , WatchMask::MODIFY) |
181 | /// .expect("Failed to add file watch" ); |
182 | /// |
183 | /// // Handle events for the file here |
184 | /// ``` |
185 | /// |
186 | /// [`inotify_add_watch`]: ../inotify_sys/fn.inotify_add_watch.html |
187 | /// [`WatchMask`]: struct.WatchMask.html |
188 | /// [`WatchDescriptor`]: struct.WatchDescriptor.html |
189 | pub fn add_watch<P>(&mut self, path: P, mask: WatchMask) |
190 | -> io::Result<WatchDescriptor> |
191 | where P: AsRef<Path> |
192 | { |
193 | let path = CString::new(path.as_ref().as_os_str().as_bytes())?; |
194 | |
195 | let wd = unsafe { |
196 | ffi::inotify_add_watch( |
197 | **self.fd, |
198 | path.as_ptr() as *const _, |
199 | mask.bits(), |
200 | ) |
201 | }; |
202 | |
203 | match wd { |
204 | -1 => Err(io::Error::last_os_error()), |
205 | _ => Ok(WatchDescriptor{ id: wd, fd: Arc::downgrade(&self.fd) }), |
206 | } |
207 | } |
208 | |
209 | /// Stops watching a file |
210 | /// |
211 | /// Removes the watch represented by the provided [`WatchDescriptor`] by |
212 | /// calling [`inotify_rm_watch`]. [`WatchDescriptor`]s can be obtained via |
213 | /// [`Inotify::add_watch`], or from the `wd` field of [`Event`]. |
214 | /// |
215 | /// # Errors |
216 | /// |
217 | /// Directly returns the error from the call to [`inotify_rm_watch`]. |
218 | /// Returns an [`io::Error`] with [`ErrorKind`]`::InvalidInput`, if the given |
219 | /// [`WatchDescriptor`] did not originate from this [`Inotify`] instance. |
220 | /// |
221 | /// # Examples |
222 | /// |
223 | /// ``` |
224 | /// use inotify::Inotify; |
225 | /// |
226 | /// let mut inotify = Inotify::init() |
227 | /// .expect("Failed to initialize an inotify instance" ); |
228 | /// |
229 | /// # // Create a temporary file, so `add_watch` won't return an error. |
230 | /// # use std::fs::File; |
231 | /// # let mut test_file = File::create("/tmp/inotify-rs-test-file" ) |
232 | /// # .expect("Failed to create test file" ); |
233 | /// # |
234 | /// # // Add a watch and modify the file, so the code below doesn't block |
235 | /// # // forever. |
236 | /// # use inotify::WatchMask; |
237 | /// # inotify.add_watch("/tmp/inotify-rs-test-file" , WatchMask::MODIFY) |
238 | /// # .expect("Failed to add file watch" ); |
239 | /// # use std::io::Write; |
240 | /// # write!(&mut test_file, "something \n" ) |
241 | /// # .expect("Failed to write something to test file" ); |
242 | /// # |
243 | /// let mut buffer = [0; 1024]; |
244 | /// let events = inotify |
245 | /// .read_events_blocking(&mut buffer) |
246 | /// .expect("Error while waiting for events" ); |
247 | /// |
248 | /// for event in events { |
249 | /// inotify.rm_watch(event.wd); |
250 | /// } |
251 | /// ``` |
252 | /// |
253 | /// [`WatchDescriptor`]: struct.WatchDescriptor.html |
254 | /// [`inotify_rm_watch`]: ../inotify_sys/fn.inotify_rm_watch.html |
255 | /// [`Inotify::add_watch`]: struct.Inotify.html#method.add_watch |
256 | /// [`Event`]: struct.Event.html |
257 | /// [`Inotify`]: struct.Inotify.html |
258 | /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html |
259 | /// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html |
260 | pub fn rm_watch(&mut self, wd: WatchDescriptor) -> io::Result<()> { |
261 | if wd.fd.upgrade().as_ref() != Some(&self.fd) { |
262 | return Err(io::Error::new( |
263 | io::ErrorKind::InvalidInput, |
264 | "Invalid WatchDescriptor" , |
265 | )); |
266 | } |
267 | |
268 | let result = unsafe { ffi::inotify_rm_watch(**self.fd, wd.id) }; |
269 | match result { |
270 | 0 => Ok(()), |
271 | -1 => Err(io::Error::last_os_error()), |
272 | _ => panic!( |
273 | "unexpected return code from inotify_rm_watch ( {})" , result) |
274 | } |
275 | } |
276 | |
277 | /// Waits until events are available, then returns them |
278 | /// |
279 | /// Blocks the current thread until at least one event is available. If this |
280 | /// is not desirable, please consider [`Inotify::read_events`]. |
281 | /// |
282 | /// This method calls [`Inotify::read_events`] internally and behaves |
283 | /// essentially the same, apart from the blocking behavior. Please refer to |
284 | /// the documentation of [`Inotify::read_events`] for more information. |
285 | /// |
286 | /// [`Inotify::read_events`]: struct.Inotify.html#method.read_events |
287 | /// [`read`]: ../libc/fn.read.html |
288 | pub fn read_events_blocking<'a>(&mut self, buffer: &'a mut [u8]) |
289 | -> io::Result<Events<'a>> |
290 | { |
291 | unsafe { |
292 | let res = fcntl(**self.fd, F_GETFL); |
293 | if res == -1 { |
294 | return Err(io::Error::last_os_error()); |
295 | } |
296 | if fcntl(**self.fd, F_SETFL, res & !O_NONBLOCK) == -1 { |
297 | return Err(io::Error::last_os_error()); |
298 | } |
299 | }; |
300 | let result = self.read_events(buffer); |
301 | unsafe { |
302 | let res = fcntl(**self.fd, F_GETFL); |
303 | if res == -1 { |
304 | return Err(io::Error::last_os_error()); |
305 | } |
306 | if fcntl(**self.fd, F_SETFL, res | O_NONBLOCK) == -1 { |
307 | return Err(io::Error::last_os_error()); |
308 | } |
309 | }; |
310 | |
311 | result |
312 | } |
313 | |
314 | /// Returns one buffer's worth of available events |
315 | /// |
316 | /// Reads as many events as possible into `buffer`, and returns an iterator |
317 | /// over them. If no events are available, an iterator is still returned. If |
318 | /// you need a method that will block until at least one event is available, |
319 | /// please consider [`read_events_blocking`]. |
320 | /// |
321 | /// Please note that inotify will merge identical successive unread events |
322 | /// into a single event. This means this method can not be used to count the |
323 | /// number of file system events. |
324 | /// |
325 | /// The `buffer` argument, as the name indicates, is used as a buffer for |
326 | /// the inotify events. Its contents may be overwritten. |
327 | /// |
328 | /// # Errors |
329 | /// |
330 | /// This function directly returns all errors from the call to [`read`] |
331 | /// (except EGAIN/EWOULDBLOCK, which result in an empty iterator). In |
332 | /// addition, [`ErrorKind::UnexpectedEof`] is returned, if the call to |
333 | /// [`read`] returns `0`, signaling end-of-file. |
334 | /// |
335 | /// If `buffer` is too small, this will result in an error with |
336 | /// [`ErrorKind::InvalidInput`]. On very old Linux kernels, |
337 | /// [`ErrorKind::UnexpectedEof`] will be returned instead. |
338 | /// |
339 | /// # Examples |
340 | /// |
341 | /// ``` |
342 | /// use inotify::Inotify; |
343 | /// |
344 | /// let mut inotify = Inotify::init() |
345 | /// .expect("Failed to initialize an inotify instance" ); |
346 | /// |
347 | /// let mut buffer = [0; 1024]; |
348 | /// let events = inotify.read_events(&mut buffer) |
349 | /// .expect("Error while reading events" ); |
350 | /// |
351 | /// for event in events { |
352 | /// // Handle event |
353 | /// } |
354 | /// ``` |
355 | /// |
356 | /// [`read_events_blocking`]: struct.Inotify.html#method.read_events_blocking |
357 | /// [`read`]: ../libc/fn.read.html |
358 | /// [`ErrorKind::UnexpectedEof`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof |
359 | /// [`ErrorKind::InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput |
360 | pub fn read_events<'a>(&mut self, buffer: &'a mut [u8]) |
361 | -> io::Result<Events<'a>> |
362 | { |
363 | let num_bytes = read_into_buffer(**self.fd, buffer); |
364 | |
365 | let num_bytes = match num_bytes { |
366 | 0 => { |
367 | return Err( |
368 | io::Error::new( |
369 | io::ErrorKind::UnexpectedEof, |
370 | "`read` return `0`, signaling end-of-file" |
371 | ) |
372 | ); |
373 | } |
374 | -1 => { |
375 | let error = io::Error::last_os_error(); |
376 | if error.kind() == io::ErrorKind::WouldBlock { |
377 | return Ok(Events::new(Arc::downgrade(&self.fd), buffer, 0)); |
378 | } |
379 | else { |
380 | return Err(error); |
381 | } |
382 | }, |
383 | _ if num_bytes < 0 => { |
384 | panic!(" {} {} {} {} {} {}" , |
385 | "Unexpected return value from `read`. Received a negative" , |
386 | "value that was not `-1`. According to the `read` man page" , |
387 | "this shouldn't happen, as either `-1` is returned on" , |
388 | "error, `0` on end-of-file, or a positive value for the" , |
389 | "number of bytes read. Returned value:" , |
390 | num_bytes, |
391 | ); |
392 | } |
393 | _ => { |
394 | // The value returned by `read` should be `isize`. Let's quickly |
395 | // verify this with the following assignment, so we can be sure |
396 | // our cast below is valid. |
397 | let num_bytes: isize = num_bytes; |
398 | |
399 | // The type returned by `read` is `isize`, and we've ruled out |
400 | // all negative values with the match arms above. This means we |
401 | // can safely cast to `usize`. |
402 | debug_assert!(num_bytes > 0); |
403 | num_bytes as usize |
404 | } |
405 | }; |
406 | |
407 | Ok(Events::new(Arc::downgrade(&self.fd), buffer, num_bytes)) |
408 | } |
409 | |
410 | /// Create a stream which collects events |
411 | /// |
412 | /// Returns a `Stream` over all events that are available. This stream is an |
413 | /// infinite source of events. |
414 | /// |
415 | /// An internal buffer which can hold the largest possible event is used. |
416 | #[cfg (feature = "stream" )] |
417 | pub fn event_stream<T>(&mut self, buffer: T) |
418 | -> io::Result<EventStream<T>> |
419 | where |
420 | T: AsMut<[u8]> + AsRef<[u8]>, |
421 | { |
422 | EventStream::new(self.fd.clone(), buffer) |
423 | } |
424 | |
425 | /// Closes the inotify instance |
426 | /// |
427 | /// Closes the file descriptor referring to the inotify instance. The user |
428 | /// usually doesn't have to call this function, as the underlying inotify |
429 | /// instance is closed automatically, when [`Inotify`] is dropped. |
430 | /// |
431 | /// # Errors |
432 | /// |
433 | /// Directly returns the error from the call to [`close`], without adding any |
434 | /// error conditions of its own. |
435 | /// |
436 | /// # Examples |
437 | /// |
438 | /// ``` |
439 | /// use inotify::Inotify; |
440 | /// |
441 | /// let mut inotify = Inotify::init() |
442 | /// .expect("Failed to initialize an inotify instance" ); |
443 | /// |
444 | /// inotify.close() |
445 | /// .expect("Failed to close inotify instance" ); |
446 | /// ``` |
447 | /// |
448 | /// [`Inotify`]: struct.Inotify.html |
449 | /// [`close`]: ../libc/fn.close.html |
450 | pub fn close(self) -> io::Result<()> { |
451 | // `self` will be dropped when this method returns. If this is the only |
452 | // owner of `fd`, the `Arc` will also be dropped. The `Drop` |
453 | // implementation for `FdGuard` will attempt to close the file descriptor |
454 | // again, unless this flag here is cleared. |
455 | self.fd.should_not_close(); |
456 | |
457 | match unsafe { ffi::close(**self.fd) } { |
458 | 0 => Ok(()), |
459 | _ => Err(io::Error::last_os_error()), |
460 | } |
461 | } |
462 | } |
463 | |
464 | impl AsRawFd for Inotify { |
465 | #[inline ] |
466 | fn as_raw_fd(&self) -> RawFd { |
467 | self.fd.as_raw_fd() |
468 | } |
469 | } |
470 | |
471 | impl FromRawFd for Inotify { |
472 | unsafe fn from_raw_fd(fd: RawFd) -> Self { |
473 | Inotify { |
474 | fd: Arc::new(data:FdGuard::from_raw_fd(fd)) |
475 | } |
476 | } |
477 | } |
478 | |
479 | impl IntoRawFd for Inotify { |
480 | #[inline ] |
481 | fn into_raw_fd(self) -> RawFd { |
482 | self.fd.should_not_close(); |
483 | self.fd.fd |
484 | } |
485 | } |
486 | |