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