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