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}