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