• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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