inotify/
inotify.rs

1use 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
19use inotify_sys as ffi;
20use libc::{
21    F_GETFL,
22    F_SETFL,
23    O_NONBLOCK,
24    fcntl,
25};
26
27use crate::events::Events;
28use crate::fd_guard::FdGuard;
29use crate::util::read_into_buffer;
30use crate::watches::{
31    WatchDescriptor,
32    WatchMask,
33    Watches,
34};
35
36
37#[cfg(feature = "stream")]
38use 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)]
52pub struct Inotify {
53    fd: Arc<FdGuard>,
54}
55
56impl 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
346impl AsRawFd for Inotify {
347    #[inline]
348    fn as_raw_fd(&self) -> RawFd {
349        self.fd.as_raw_fd()
350    }
351}
352
353impl FromRawFd for Inotify {
354    unsafe fn from_raw_fd(fd: RawFd) -> Self {
355        Inotify {
356            fd: Arc::new(FdGuard::from_raw_fd(fd))
357        }
358    }
359}
360
361impl 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
369impl AsFd for Inotify {
370    #[inline]
371    fn as_fd(&self) -> BorrowedFd<'_> {
372        self.fd.as_fd()
373    }
374}
375
376impl From<Inotify> for OwnedFd {
377    fn from(fd: Inotify) -> OwnedFd {
378        unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) }
379    }
380}
381
382impl From<OwnedFd> for Inotify {
383    fn from(fd: OwnedFd) -> Inotify {
384        unsafe { Inotify::from_raw_fd(fd.into_raw_fd()) }
385    }
386}