1 #![deny(trivial_numeric_casts, unstable_features, unused_import_braces, unused_qualifications)]
2 #![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
3 
4 #[macro_use]
5 extern crate bitflags;
6 
7 extern crate fsevent_sys as fsevent;
8 
9 use fsevent::core_foundation as cf;
10 use fsevent as fs;
11 
12 use std::convert::AsRef;
13 use std::ffi::CStr;
14 use std::ptr;
15 use std::slice;
16 use std::slice::from_raw_parts_mut;
17 use std::str::from_utf8;
18 
19 use std::sync::mpsc::Sender;
20 
21 #[cfg(target_pointer_width = "64")]
22 type SafePointer = u64;
23 
24 #[cfg(target_pointer_width = "32")]
25 type SafePointer = u32;
26 
27 #[derive(Clone, Copy, Debug)]
28 pub struct FsEventRefWrapper {
29     ptr: SafePointer,
30 }
31 
32 impl From<*mut ::std::os::raw::c_void> for FsEventRefWrapper {
from(raw: *mut ::std::os::raw::c_void) -> FsEventRefWrapper33     fn from(raw: *mut ::std::os::raw::c_void) -> FsEventRefWrapper {
34         let ptr = raw as SafePointer;
35         Self { ptr }
36     }
37 }
38 
39 impl From<FsEventRefWrapper> for *mut ::std::os::raw::c_void {
from(safe: FsEventRefWrapper) -> *mut ::std::os::raw::c_void40     fn from(safe: FsEventRefWrapper) -> *mut ::std::os::raw::c_void {
41         safe.ptr as *mut ::std::os::raw::c_void
42     }
43 }
44 
45 pub struct FsEvent {
46     paths: Vec<String>,
47     since_when: fs::FSEventStreamEventId,
48     latency: cf::CFTimeInterval,
49     flags: fs::FSEventStreamCreateFlags,
50 }
51 
52 #[derive(Debug)]
53 pub struct Event {
54     pub event_id: u64,
55     pub flag: StreamFlags,
56     pub path: String,
57 }
58 
59 // Synchronize with
60 // /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/Headers/FSEvents.h
61 bitflags! {
62   #[repr(C)]
63   pub struct StreamFlags: u32 {
64     const NONE = 0x00000000;
65     const MUST_SCAN_SUBDIRS = 0x00000001;
66     const USER_DROPPED = 0x00000002;
67     const KERNEL_DROPPED = 0x00000004;
68     const IDS_WRAPPED = 0x00000008;
69     const HISTORY_DONE = 0x00000010;
70     const ROOT_CHANGED = 0x00000020;
71     const MOUNT = 0x00000040;
72     const UNMOUNT = 0x00000080;
73     const ITEM_CREATED = 0x00000100;
74     const ITEM_REMOVED = 0x00000200;
75     const INOTE_META_MOD = 0x00000400;
76     const ITEM_RENAMED = 0x00000800;
77     const ITEM_MODIFIED = 0x00001000;
78     const FINDER_INFO_MOD = 0x00002000;
79     const ITEM_CHANGE_OWNER = 0x00004000;
80     const ITEM_XATTR_MOD = 0x00008000;
81     const IS_FILE = 0x00010000;
82     const IS_DIR = 0x00020000;
83     const IS_SYMLIMK = 0x00040000;
84     const OWN_EVENT = 0x00080000;
85     const IS_HARDLINK = 0x00100000;
86     const IS_LAST_HARDLINK = 0x00200000;
87     const ITEM_CLONED = 0x400000;
88   }
89 }
90 
91 impl std::fmt::Display for StreamFlags {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result92     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93         if self.contains(StreamFlags::MUST_SCAN_SUBDIRS) {
94             let _d = write!(f, "MUST_SCAN_SUBDIRS ");
95         }
96         if self.contains(StreamFlags::USER_DROPPED) {
97             let _d = write!(f, "USER_DROPPED ");
98         }
99         if self.contains(StreamFlags::KERNEL_DROPPED) {
100             let _d = write!(f, "KERNEL_DROPPED ");
101         }
102         if self.contains(StreamFlags::IDS_WRAPPED) {
103             let _d = write!(f, "IDS_WRAPPED ");
104         }
105         if self.contains(StreamFlags::HISTORY_DONE) {
106             let _d = write!(f, "HISTORY_DONE ");
107         }
108         if self.contains(StreamFlags::ROOT_CHANGED) {
109             let _d = write!(f, "ROOT_CHANGED ");
110         }
111         if self.contains(StreamFlags::MOUNT) {
112             let _d = write!(f, "MOUNT ");
113         }
114         if self.contains(StreamFlags::UNMOUNT) {
115             let _d = write!(f, "UNMOUNT ");
116         }
117         if self.contains(StreamFlags::ITEM_CREATED) {
118             let _d = write!(f, "ITEM_CREATED ");
119         }
120         if self.contains(StreamFlags::ITEM_REMOVED) {
121             let _d = write!(f, "ITEM_REMOVED ");
122         }
123         if self.contains(StreamFlags::INOTE_META_MOD) {
124             let _d = write!(f, "INOTE_META_MOD ");
125         }
126         if self.contains(StreamFlags::ITEM_RENAMED) {
127             let _d = write!(f, "ITEM_RENAMED ");
128         }
129         if self.contains(StreamFlags::ITEM_MODIFIED) {
130             let _d = write!(f, "ITEM_MODIFIED ");
131         }
132         if self.contains(StreamFlags::FINDER_INFO_MOD) {
133             let _d = write!(f, "FINDER_INFO_MOD ");
134         }
135         if self.contains(StreamFlags::ITEM_CHANGE_OWNER) {
136             let _d = write!(f, "ITEM_CHANGE_OWNER ");
137         }
138         if self.contains(StreamFlags::ITEM_XATTR_MOD) {
139             let _d = write!(f, "ITEM_XATTR_MOD ");
140         }
141         if self.contains(StreamFlags::IS_FILE) {
142             let _d = write!(f, "IS_FILE ");
143         }
144         if self.contains(StreamFlags::IS_DIR) {
145             let _d = write!(f, "IS_DIR ");
146         }
147         if self.contains(StreamFlags::IS_SYMLIMK) {
148             let _d = write!(f, "IS_SYMLIMK ");
149         }
150         if self.contains(StreamFlags::OWN_EVENT) {
151             let _d = write!(f, "OWN_EVENT ");
152         }
153         if self.contains(StreamFlags::IS_LAST_HARDLINK) {
154             let _d = write!(f, "IS_LAST_HARDLINK ");
155         }
156         if self.contains(StreamFlags::IS_HARDLINK) {
157             let _d = write!(f, "IS_HARDLINK ");
158         }
159         if self.contains(StreamFlags::ITEM_CLONED) {
160             let _d = write!(f, "ITEM_CLONED ");
161         }
162         write!(f, "")
163     }
164 }
165 
default_stream_context(event_sender: *const Sender<Event>) -> fs::FSEventStreamContext166 fn default_stream_context(event_sender: *const Sender<Event>) -> fs::FSEventStreamContext {
167     let ptr = event_sender as *mut ::std::os::raw::c_void;
168     fs::FSEventStreamContext {
169         version: 0,
170         info: ptr,
171         retain: cf::NULL,
172         copy_description: cf::NULL,
173     }
174 }
175 
176 pub type Result<T> = std::result::Result<T, Error>;
177 
178 #[derive(Debug)]
179 pub struct Error {
180     msg: String,
181 }
182 
183 impl std::error::Error for Error {
description(&self) -> &str184     fn description(&self) -> &str {
185         &self.msg
186     }
187 }
188 
189 impl std::fmt::Display for Error {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result190     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
191         self.msg.fmt(f)
192     }
193 }
194 
195 impl From<std::sync::mpsc::RecvTimeoutError> for Error {
from(err: std::sync::mpsc::RecvTimeoutError) -> Error196     fn from(err: std::sync::mpsc::RecvTimeoutError) -> Error {
197         use std::error::Error;
198 
199         Self {
200             msg: err.description().to_string(),
201         }
202     }
203 }
204 
205 impl FsEvent {
new(paths: Vec<String>) -> Self206     pub fn new(paths: Vec<String>) -> Self {
207         Self {
208             paths,
209             since_when: fs::kFSEventStreamEventIdSinceNow,
210             latency: 0.0,
211             flags: fs::kFSEventStreamCreateFlagFileEvents | fs::kFSEventStreamCreateFlagNoDefer,
212         }
213     }
214 
215     // https://github.com/thibaudgg/rb-fsevent/blob/master/ext/fsevent_watch/main.c
append_path(&mut self, source: &str) -> Result<()>216     pub fn append_path(&mut self, source: &str) -> Result<()> {
217         self.paths.push(source.to_string());
218         Ok(())
219     }
220 
build_native_paths(&self) -> Result<cf::CFMutableArrayRef>221     fn build_native_paths(&self) -> Result<cf::CFMutableArrayRef> {
222         let native_paths = unsafe {
223             cf::CFArrayCreateMutable(cf::kCFAllocatorDefault, 0, &cf::kCFTypeArrayCallBacks)
224         };
225 
226         if native_paths == std::ptr::null_mut() {
227             Err(Error {
228                 msg: "Unable to allocate CFMutableArrayRef".to_string(),
229             })
230         } else {
231             for path in &self.paths {
232                 unsafe {
233                     let mut err = ptr::null_mut();
234                     let cf_path = cf::str_path_to_cfstring_ref(path, &mut err);
235                     if !err.is_null() {
236                         let cf_str = cf::CFCopyDescription(err as cf::CFRef);
237                         let mut buf = [0; 1024];
238                         cf::CFStringGetCString(
239                             cf_str,
240                             buf.as_mut_ptr(),
241                             buf.len() as cf::CFIndex,
242                             cf::kCFStringEncodingUTF8,
243                         );
244                         return Err(Error {
245                             msg: CStr::from_ptr(buf.as_ptr())
246                                 .to_str()
247                                 .unwrap_or("Unknown error")
248                                 .to_string(),
249                         });
250                     } else {
251                         cf::CFArrayAppendValue(native_paths, cf_path);
252                         cf::CFRelease(cf_path);
253                     }
254                 }
255             }
256 
257             Ok(native_paths)
258         }
259     }
260 
internal_observe( since_when: fs::FSEventStreamEventId, latency: cf::CFTimeInterval, flags: fs::FSEventStreamCreateFlags, paths: FsEventRefWrapper, event_sender: Sender<Event>, subscription_handle_sender: Option<Sender<FsEventRefWrapper>>, ) -> Result<()>261     fn internal_observe(
262         since_when: fs::FSEventStreamEventId,
263         latency: cf::CFTimeInterval,
264         flags: fs::FSEventStreamCreateFlags,
265         paths: FsEventRefWrapper,
266         event_sender: Sender<Event>,
267         subscription_handle_sender: Option<Sender<FsEventRefWrapper>>,
268     ) -> Result<()> {
269         let stream_context = default_stream_context(&event_sender);
270         let cb = callback as *mut _;
271         let paths = paths.into();
272 
273         unsafe {
274             let stream = fs::FSEventStreamCreate(
275                 cf::kCFAllocatorDefault,
276                 cb,
277                 &stream_context,
278                 paths,
279                 since_when,
280                 latency,
281                 flags,
282             );
283 
284             // fs::FSEventStreamShow(stream);
285 
286             match subscription_handle_sender {
287                 Some(ret_tx) => {
288                     let runloop_ref = cf::CFRunLoopGetCurrent();
289                     let runloop_ref_safe = FsEventRefWrapper::from(runloop_ref);
290                     let ptr_val = runloop_ref_safe.ptr.clone();
291                     ret_tx
292                         .send(runloop_ref_safe)
293                         .expect(&format!("Unable to return CFRunLoopRef ({:#X})", ptr_val));
294                 }
295                 None => {}
296             }
297 
298             fs::FSEventStreamScheduleWithRunLoop(
299                 stream,
300                 cf::CFRunLoopGetCurrent(),
301                 cf::kCFRunLoopDefaultMode,
302             );
303 
304             fs::FSEventStreamStart(stream);
305             cf::CFRunLoopRun();
306 
307             fs::FSEventStreamFlushSync(stream);
308             fs::FSEventStreamStop(stream);
309         }
310 
311         Ok(())
312     }
313 
observe(&self, event_sender: Sender<Event>)314     pub fn observe(&self, event_sender: Sender<Event>) {
315         let native_paths = self.build_native_paths()
316             .expect("Unable to build CFMutableArrayRef of watched paths.");
317         let safe_native_paths = FsEventRefWrapper::from(native_paths);
318         Self::internal_observe(
319             self.since_when,
320             self.latency,
321             self.flags,
322             safe_native_paths,
323             event_sender,
324             None,
325         ).unwrap();
326     }
327 
observe_async(&self, event_sender: Sender<Event>) -> Result<FsEventRefWrapper>328     pub fn observe_async(&self, event_sender: Sender<Event>) -> Result<FsEventRefWrapper> {
329         let (ret_tx, ret_rx) = std::sync::mpsc::channel();
330         let native_paths = self.build_native_paths()?;
331         let safe_native_paths = FsEventRefWrapper::from(native_paths);
332 
333         let since_when = self.since_when;
334         let latency = self.latency;
335         let flags = self.flags;
336         std::thread::spawn(move || {
337             Self::internal_observe(
338                 since_when,
339                 latency,
340                 flags,
341                 safe_native_paths,
342                 event_sender,
343                 Some(ret_tx),
344             )
345         });
346 
347         match ret_rx.recv_timeout(std::time::Duration::from_secs(5)) {
348             Ok(v) => Ok(v),
349             Err(e) => Err(Error::from(e)),
350         }
351     }
352 
shutdown_observe(&self, handle: FsEventRefWrapper)353     pub fn shutdown_observe(&self, handle: FsEventRefWrapper) {
354         unsafe { cf::CFRunLoopStop(handle.into()) };
355     }
356 }
357 
358 #[allow(unused_variables)]
callback( stream_ref: fs::FSEventStreamRef, info: *mut ::std::os::raw::c_void, num_events: usize, event_paths: *const *const ::std::os::raw::c_char, event_flags: *mut ::std::os::raw::c_void, event_ids: *mut ::std::os::raw::c_void, )359 unsafe fn callback(
360     stream_ref: fs::FSEventStreamRef,
361     info: *mut ::std::os::raw::c_void,
362     num_events: usize,                                 // size_t numEvents
363     event_paths: *const *const ::std::os::raw::c_char, // void *eventPaths
364     event_flags: *mut ::std::os::raw::c_void,          // const FSEventStreamEventFlags eventFlags[]
365     event_ids: *mut ::std::os::raw::c_void,            // const FSEventStreamEventId eventIds[]
366 ) {
367     let num = num_events;
368     let e_ptr = event_flags as *mut u32;
369     let i_ptr = event_ids as *mut u64;
370     let sender = info as *mut Sender<Event>;
371 
372     let paths: &[*const ::std::os::raw::c_char] =
373         std::mem::transmute(slice::from_raw_parts(event_paths, num));
374     let flags = from_raw_parts_mut(e_ptr, num);
375     let ids = from_raw_parts_mut(i_ptr, num);
376 
377     for p in 0..num {
378         let i = CStr::from_ptr(paths[p]).to_bytes();
379         let path = from_utf8(i).expect("Invalid UTF8 string.");
380         let flag: StreamFlags = StreamFlags::from_bits(flags[p])
381             .expect(format!("Unable to decode StreamFlags: {} for {}", flags[p], path).as_ref());
382         // println!("{}: {}", ids[p], flag);
383 
384         let event = Event {
385             event_id: ids[p],
386             flag,
387             path: path.to_string(),
388         };
389         let _s = (*sender).send(event);
390     }
391 }
392