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). init(flags: InitFlags) -> Result<Inotify>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). add_watch<P: ?Sized + NixPath>(self, path: &P, mask: AddWatchFlags) -> Result<WatchDescriptor>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")] rm_watch(self, wd: WatchDescriptor) -> Result<()>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")] rm_watch(self, wd: WatchDescriptor) -> Result<()>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. read_events(self) -> Result<Vec<InotifyEvent>>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 { as_raw_fd(&self) -> RawFd224 fn as_raw_fd(&self) -> RawFd { 225 self.fd 226 } 227 } 228 229 impl FromRawFd for Inotify { from_raw_fd(fd: RawFd) -> Self230 unsafe fn from_raw_fd(fd: RawFd) -> Self { 231 Inotify { fd } 232 } 233 } 234