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}