| Andrew Walbran | 12f6140 | 2020-10-14 11:10:53 +0100 | [diff] [blame^] | 1 | //! Monitoring API for filesystem events. |
| 2 | //! |
| 3 | //! Inotify is a Linux-only API to monitor filesystems events. |
| 4 | //! |
| 5 | //! For more documentation, please read [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html). |
| 6 | //! |
| 7 | //! # Examples |
| 8 | //! |
| 9 | //! Monitor all events happening in directory "test": |
| 10 | //! ```no_run |
| 11 | //! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify}; |
| 12 | //! # |
| 13 | //! // We create a new inotify instance. |
| 14 | //! let instance = Inotify::init(InitFlags::empty()).unwrap(); |
| 15 | //! |
| 16 | //! // We add a new watch on directory "test" for all events. |
| 17 | //! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap(); |
| 18 | //! |
| 19 | //! loop { |
| 20 | //! // We read from our inotify instance for events. |
| 21 | //! let events = instance.read_events().unwrap(); |
| 22 | //! println!("Events: {:?}", events); |
| 23 | //! } |
| 24 | //! ``` |
| 25 | |
| 26 | use libc::{ |
| 27 | c_char, |
| 28 | c_int, |
| 29 | }; |
| 30 | use std::ffi::{OsString,OsStr,CStr}; |
| 31 | use std::os::unix::ffi::OsStrExt; |
| 32 | use std::mem::{MaybeUninit, size_of}; |
| 33 | use std::os::unix::io::{RawFd,AsRawFd,FromRawFd}; |
| 34 | use std::ptr; |
| 35 | use crate::unistd::read; |
| 36 | use crate::Result; |
| 37 | use crate::NixPath; |
| 38 | use crate::errno::Errno; |
| 39 | |
| 40 | libc_bitflags! { |
| 41 | /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html). |
| 42 | pub struct AddWatchFlags: u32 { |
| 43 | IN_ACCESS; |
| 44 | IN_MODIFY; |
| 45 | IN_ATTRIB; |
| 46 | IN_CLOSE_WRITE; |
| 47 | IN_CLOSE_NOWRITE; |
| 48 | IN_OPEN; |
| 49 | IN_MOVED_FROM; |
| 50 | IN_MOVED_TO; |
| 51 | IN_CREATE; |
| 52 | IN_DELETE; |
| 53 | IN_DELETE_SELF; |
| 54 | IN_MOVE_SELF; |
| 55 | |
| 56 | IN_UNMOUNT; |
| 57 | IN_Q_OVERFLOW; |
| 58 | IN_IGNORED; |
| 59 | |
| 60 | IN_CLOSE; |
| 61 | IN_MOVE; |
| 62 | |
| 63 | IN_ONLYDIR; |
| 64 | IN_DONT_FOLLOW; |
| 65 | |
| 66 | IN_ISDIR; |
| 67 | IN_ONESHOT; |
| 68 | IN_ALL_EVENTS; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | libc_bitflags! { |
| 73 | /// Configuration options for [`inotify_init1`](fn.inotify_init1.html). |
| 74 | pub struct InitFlags: c_int { |
| 75 | IN_CLOEXEC; |
| 76 | IN_NONBLOCK; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /// An inotify instance. This is also a file descriptor, you can feed it to |
| 81 | /// other interfaces consuming file descriptors, epoll for example. |
| 82 | #[derive(Debug, Clone, Copy)] |
| 83 | pub struct Inotify { |
| 84 | fd: RawFd |
| 85 | } |
| 86 | |
| 87 | /// This object is returned when you create a new watch on an inotify instance. |
| 88 | /// It is then returned as part of an event once triggered. It allows you to |
| 89 | /// know which watch triggered which event. |
| 90 | #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] |
| 91 | pub struct WatchDescriptor { |
| 92 | wd: i32 |
| 93 | } |
| 94 | |
| 95 | /// A single inotify event. |
| 96 | /// |
| 97 | /// For more documentation see, [inotify(7)](http://man7.org/linux/man-pages/man7/inotify.7.html). |
| 98 | #[derive(Debug)] |
| 99 | pub struct InotifyEvent { |
| 100 | /// Watch descriptor. This field corresponds to the watch descriptor you |
| 101 | /// were issued when calling add_watch. It allows you to know which watch |
| 102 | /// this event comes from. |
| 103 | pub wd: WatchDescriptor, |
| 104 | /// Event mask. This field is a bitfield describing the exact event that |
| 105 | /// occured. |
| 106 | pub mask: AddWatchFlags, |
| 107 | /// This cookie is a number that allows you to connect related events. For |
| 108 | /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected. |
| 109 | pub cookie: u32, |
| 110 | /// Filename. This field exists only if the event was triggered for a file |
| 111 | /// inside the watched directory. |
| 112 | pub name: Option<OsString> |
| 113 | } |
| 114 | |
| 115 | impl Inotify { |
| 116 | /// Initialize a new inotify instance. |
| 117 | /// |
| 118 | /// Returns a Result containing an inotify instance. |
| 119 | /// |
| 120 | /// For more information see, [inotify_init(2)](http://man7.org/linux/man-pages/man2/inotify_init.2.html). |
| 121 | pub fn init(flags: InitFlags) -> Result<Inotify> { |
| 122 | let res = Errno::result(unsafe { |
| 123 | libc::inotify_init1(flags.bits()) |
| 124 | }); |
| 125 | |
| 126 | res.map(|fd| Inotify { fd }) |
| 127 | } |
| 128 | |
| 129 | /// Adds a new watch on the target file or directory. |
| 130 | /// |
| 131 | /// Returns a watch descriptor. This is not a File Descriptor! |
| 132 | /// |
| 133 | /// For more information see, [inotify_add_watch(2)](http://man7.org/linux/man-pages/man2/inotify_add_watch.2.html). |
| 134 | pub fn add_watch<P: ?Sized + NixPath>(self, |
| 135 | path: &P, |
| 136 | mask: AddWatchFlags) |
| 137 | -> Result<WatchDescriptor> |
| 138 | { |
| 139 | let res = path.with_nix_path(|cstr| { |
| 140 | unsafe { |
| 141 | libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits()) |
| 142 | } |
| 143 | })?; |
| 144 | |
| 145 | Errno::result(res).map(|wd| WatchDescriptor { wd }) |
| 146 | } |
| 147 | |
| 148 | /// Removes an existing watch using the watch descriptor returned by |
| 149 | /// inotify_add_watch. |
| 150 | /// |
| 151 | /// Returns an EINVAL error if the watch descriptor is invalid. |
| 152 | /// |
| 153 | /// For more information see, [inotify_rm_watch(2)](http://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html). |
| 154 | #[cfg(target_os = "linux")] |
| 155 | pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> { |
| 156 | let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd) }; |
| 157 | |
| 158 | Errno::result(res).map(drop) |
| 159 | } |
| 160 | |
| 161 | #[cfg(target_os = "android")] |
| 162 | pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> { |
| 163 | let res = unsafe { libc::inotify_rm_watch(self.fd, wd.wd as u32) }; |
| 164 | |
| 165 | Errno::result(res).map(drop) |
| 166 | } |
| 167 | |
| 168 | /// Reads a collection of events from the inotify file descriptor. This call |
| 169 | /// can either be blocking or non blocking depending on whether IN_NONBLOCK |
| 170 | /// was set at initialization. |
| 171 | /// |
| 172 | /// Returns as many events as available. If the call was non blocking and no |
| 173 | /// events could be read then the EAGAIN error is returned. |
| 174 | pub fn read_events(self) -> Result<Vec<InotifyEvent>> { |
| 175 | let header_size = size_of::<libc::inotify_event>(); |
| 176 | const BUFSIZ: usize = 4096; |
| 177 | let mut buffer = [0u8; BUFSIZ]; |
| 178 | let mut events = Vec::new(); |
| 179 | let mut offset = 0; |
| 180 | |
| 181 | let nread = read(self.fd, &mut buffer)?; |
| 182 | |
| 183 | while (nread - offset) >= header_size { |
| 184 | let event = unsafe { |
| 185 | let mut event = MaybeUninit::<libc::inotify_event>::uninit(); |
| 186 | ptr::copy_nonoverlapping( |
| 187 | buffer.as_ptr().add(offset), |
| 188 | event.as_mut_ptr() as *mut u8, |
| 189 | (BUFSIZ - offset).min(header_size) |
| 190 | ); |
| 191 | event.assume_init() |
| 192 | }; |
| 193 | |
| 194 | let name = match event.len { |
| 195 | 0 => None, |
| 196 | _ => { |
| 197 | let ptr = unsafe { |
| 198 | buffer |
| 199 | .as_ptr() |
| 200 | .add(offset + header_size) |
| 201 | as *const c_char |
| 202 | }; |
| 203 | let cstr = unsafe { CStr::from_ptr(ptr) }; |
| 204 | |
| 205 | Some(OsStr::from_bytes(cstr.to_bytes()).to_owned()) |
| 206 | } |
| 207 | }; |
| 208 | |
| 209 | events.push(InotifyEvent { |
| 210 | wd: WatchDescriptor { wd: event.wd }, |
| 211 | mask: AddWatchFlags::from_bits_truncate(event.mask), |
| 212 | cookie: event.cookie, |
| 213 | name |
| 214 | }); |
| 215 | |
| 216 | offset += header_size + event.len as usize; |
| 217 | } |
| 218 | |
| 219 | Ok(events) |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | impl AsRawFd for Inotify { |
| 224 | fn as_raw_fd(&self) -> RawFd { |
| 225 | self.fd |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | impl FromRawFd for Inotify { |
| 230 | unsafe fn from_raw_fd(fd: RawFd) -> Self { |
| 231 | Inotify { fd } |
| 232 | } |
| 233 | } |