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 use libc::{ 28 c_char, 29 c_int, 30 }; 31 use std::ffi::{OsString,OsStr,CStr}; 32 use std::os::unix::ffi::OsStrExt; 33 use std::mem::size_of; 34 use std::os::unix::io::{RawFd,AsRawFd,FromRawFd}; 35 use unistd::read; 36 use Result; 37 use NixPath; 38 use 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 let mut buffer = [0u8; 4096]; 177 let mut events = Vec::new(); 178 let mut offset = 0; 179 180 let nread = read(self.fd, &mut buffer)?; 181 182 while (nread - offset) >= header_size { 183 let event = unsafe { 184 &*( 185 buffer 186 .as_ptr() 187 .offset(offset as isize) as *const libc::inotify_event 188 ) 189 }; 190 191 let name = match event.len { 192 0 => None, 193 _ => { 194 let ptr = unsafe { 195 buffer 196 .as_ptr() 197 .offset(offset as isize + header_size as isize) 198 as *const c_char 199 }; 200 let cstr = unsafe { CStr::from_ptr(ptr) }; 201 202 Some(OsStr::from_bytes(cstr.to_bytes()).to_owned()) 203 } 204 }; 205 206 events.push(InotifyEvent { 207 wd: WatchDescriptor { wd: event.wd }, 208 mask: AddWatchFlags::from_bits_truncate(event.mask), 209 cookie: event.cookie, 210 name 211 }); 212 213 offset += header_size + event.len as usize; 214 } 215 216 Ok(events) 217 } 218 } 219 220 impl AsRawFd for Inotify { as_raw_fd(&self) -> RawFd221 fn as_raw_fd(&self) -> RawFd { 222 self.fd 223 } 224 } 225 226 impl FromRawFd for Inotify { from_raw_fd(fd: RawFd) -> Self227 unsafe fn from_raw_fd(fd: RawFd) -> Self { 228 Inotify { fd } 229 } 230 } 231