1use 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
18use inotify_sys as ffi;
19use libc::{
20 F_GETFL,
21 F_SETFD,
22 F_SETFL,
23 FD_CLOEXEC,
24 O_NONBLOCK,
25 fcntl,
26};
27
28use crate::events::Events;
29use crate::fd_guard::FdGuard;
30use crate::util::read_into_buffer;
31use crate::watches::{
32 WatchDescriptor,
33 WatchMask,
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]: index.html
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`]: 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
464impl AsRawFd for Inotify {
465 #[inline]
466 fn as_raw_fd(&self) -> RawFd {
467 self.fd.as_raw_fd()
468 }
469}
470
471impl 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
479impl 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