inotify/
inotify.rs

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