on eaaefcf help sign in
294 lines 8.3 kB history raw
1/*
2 * Code adapted from the Haskell "hfsevents" package.
3 *
4 * Copyright (c) 2012, Luite Stegeman
5 *
6 */
8#include <config.h>
10#if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGFILEEVENTS
12#include <CoreServices/CoreServices.h>
13#include <pthread.h>
14#include <unistd.h>
15#include <FileSystem/Event/Darwin.h>
17/*
18 * For reference documentaion see:
19 * https://developer.apple.com/documentation/coreservices/file_system_events?language=objc
20 * https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
21 *
22 * An OS thread is started which runs the event loop. A pipe is created
23 * and the events are sent to the pipes. The receiver can read the pipe
24 * output end to get the events.
25 */
27/******************************************************************************
28 * Create Flags
29 *****************************************************************************/
31UInt32 FSEventStreamCreateFlagNoDefer () {
32 return kFSEventStreamCreateFlagNoDefer;
34UInt32 FSEventStreamCreateFlagWatchRoot () {
35 return kFSEventStreamCreateFlagWatchRoot;
37UInt32 FSEventStreamCreateFlagFileEvents () {
38 return kFSEventStreamCreateFlagFileEvents;
40UInt32 FSEventStreamCreateFlagIgnoreSelf () {
41 return kFSEventStreamCreateFlagIgnoreSelf;
43#if 0
44UInt32 FSEventStreamCreateFlagFullHistory = kFSEventStreamCreateFlagFullHistory;
45#endif
47/******************************************************************************
48 * Event Flags
49 *****************************************************************************/
51UInt32 FSEventStreamEventFlagEventIdsWrapped () {
52 return kFSEventStreamEventFlagEventIdsWrapped;
54UInt32 FSEventStreamEventFlagMustScanSubDirs () {
55 return kFSEventStreamEventFlagMustScanSubDirs;
57UInt32 FSEventStreamEventFlagKernelDropped () {
58 return kFSEventStreamEventFlagKernelDropped;
60UInt32 FSEventStreamEventFlagUserDropped () {
61 return kFSEventStreamEventFlagUserDropped;
63UInt32 FSEventStreamEventFlagHistoryDone () {
64 return kFSEventStreamEventFlagHistoryDone;
66UInt32 FSEventStreamEventFlagRootChanged () {
67 return kFSEventStreamEventFlagRootChanged;
69UInt32 FSEventStreamEventFlagMount () {
70 return kFSEventStreamEventFlagMount;
72UInt32 FSEventStreamEventFlagUnmount () {
73 return kFSEventStreamEventFlagUnmount;
75UInt32 FSEventStreamEventFlagItemChangeOwner () {
76 return kFSEventStreamEventFlagItemChangeOwner;
78UInt32 FSEventStreamEventFlagItemInodeMetaMod () {
79 return kFSEventStreamEventFlagItemInodeMetaMod;
81UInt32 FSEventStreamEventFlagItemFinderInfoMod () {
82 return kFSEventStreamEventFlagItemFinderInfoMod;
84UInt32 FSEventStreamEventFlagItemXattrMod () {
85 return kFSEventStreamEventFlagItemXattrMod;
87UInt32 FSEventStreamEventFlagItemCreated () {
88 return kFSEventStreamEventFlagItemCreated;
90UInt32 FSEventStreamEventFlagItemRemoved () {
91 return kFSEventStreamEventFlagItemRemoved;
93UInt32 FSEventStreamEventFlagItemRenamed () {
94 return kFSEventStreamEventFlagItemRenamed;
96UInt32 FSEventStreamEventFlagItemModified () {
97 return kFSEventStreamEventFlagItemModified;
99#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
100UInt32 FSEventStreamEventFlagItemCloned () {
101 return kFSEventStreamEventFlagItemCloned;
103#endif
104UInt32 FSEventStreamEventFlagItemIsDir () {
105 return kFSEventStreamEventFlagItemIsDir;
107UInt32 FSEventStreamEventFlagItemIsFile () {
108 return kFSEventStreamEventFlagItemIsFile;
110UInt32 FSEventStreamEventFlagItemIsSymlink () {
111 return kFSEventStreamEventFlagItemIsSymlink;
113#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
114UInt32 FSEventStreamEventFlagItemIsHardlink () {
115 return kFSEventStreamEventFlagItemIsHardlink;
117#endif
118UInt32 FSEventStreamEventFlagItemIsLastHardlink () {
119 return kFSEventStreamEventFlagItemIsLastHardlink;
122/******************************************************************************
123 * Event watch
124 *****************************************************************************/
126/* Write an event to the pipe input fd */
127static void writeEvent(int fd, UInt64 eventId, UInt64 eventFlags, char* path)
129 UInt64 buf[3];
130 buf[0] = eventId;
131 buf[1] = eventFlags;
132 /* XXX Is the path string in UTF-8? */
133 buf[2] = (UInt64)strlen(path);
134 write(fd, buf, 3 * sizeof(UInt64));
135 write(fd, path, strlen(path));
138/* thread state */
139struct watch
141 FSEventStreamRef eventStream;
142 CFRunLoopRef runLoop;
143 int writefd;
144 pthread_mutex_t mut;
145};
147/* Just writes the event to the pipe input fd */
148static void watchCallback
149 ( ConstFSEventStreamRef streamRef
150 , void *clientCallBackInfo
151 , size_t n
152 , void *eventPaths
153 , const FSEventStreamEventFlags eventFlags[]
154 , const FSEventStreamEventId eventIds[]
155 )
157 int i;
158 struct watch *w = clientCallBackInfo;
159 char **paths = eventPaths;
161 for (i = 0; i < n; i++) {
162 writeEvent(w->writefd, eventIds[i], eventFlags[i], paths[i]);
163 }
166/******************************************************************************
167 * Start a watch event loop
168 *****************************************************************************/
170/* Event loop run in a pthread */
171static void *watchRunLoop(void *vw)
173 struct watch* w = (struct watch*) vw;
174 CFRunLoopRef rl = CFRunLoopGetCurrent();
175 CFRetain(rl);
176 w->runLoop = rl;
177 FSEventStreamScheduleWithRunLoop(w->eventStream, rl, kCFRunLoopDefaultMode);
178 FSEventStreamStart(w->eventStream);
179 pthread_mutex_unlock(&w->mut);
180 CFRunLoopRun();
181 pthread_exit(NULL);
184#define MAX_WATCH_PATHS 4096
186int createWatch
187 ( struct pathName* folders
188 , int n /* number of entries in folders */
189 , UInt32 createFlags
190 , UInt64 since
191 , double latency
192 , int* fd
193 , void** wp
194 )
196 if (n > MAX_WATCH_PATHS) {
197 return -1;
198 }
200 int pfds[2];
201 if (pipe (pfds)) {
202 return -1;
203 }
205 /*
206 * XXX We can possibly use since == 0 to get all events since
207 * beginning of time
208 */
209 if (!since) {
210 since = kFSEventStreamEventIdSinceNow;
211 }
213 /* Setup paths array */
214 CFStringRef *cffolders = malloc(n * sizeof(CFStringRef));
215 int i;
216 for(i = 0; i < n; i++) {
217 cffolders[i] = CFStringCreateWithBytes
218 ( NULL
219 , folders[i].pathBytes
220 , folders[i].pathLen
221 , kCFStringEncodingUTF8
222 , false
223 );
224 }
225 CFArrayRef paths = CFArrayCreate(NULL, (const void **)cffolders, n, NULL);
227 /* Setup context */
228 struct watch *w = malloc(sizeof(struct watch));
229 FSEventStreamContext ctx;
230 ctx.version = 0;
231 ctx.info = (void*)w;
232 ctx.retain = NULL;
233 ctx.release = NULL;
234 ctx.copyDescription = NULL;
236 /* Create watch using paths and context*/
237 FSEventStreamRef es = FSEventStreamCreate
238 (NULL, &watchCallback, &ctx, paths, since, latency, createFlags);
240 /* Run the event loop in a pthread */
241 int retval;
242 if(es != NULL) {
243 /* Success */
244 w->writefd = pfds[1];
245 w->eventStream = es;
246 w->runLoop = NULL;
248 /* Lock to prevent race against watch destroy */
249 pthread_mutex_init(&w->mut, NULL);
250 pthread_mutex_lock(&w->mut);
251 pthread_t t;
252 pthread_create(&t, NULL, &watchRunLoop, (void*)w);
254 /* return the out fd and the watch struct */
255 *fd = pfds[0];
256 *wp = w;
257 retval = 0;
258 } else {
259 /* Failure */
260 close(pfds[0]);
261 close(pfds[1]);
262 free(w);
263 retval = -1;
264 }
266 /* Cleanup */
267 for (i = 0; i < n; i++) {
268 CFRelease (cffolders[i]);
269 }
270 free(cffolders);
271 CFRelease(paths);
272 return retval;
275/******************************************************************************
276 * Stop a watch event loop
277 *****************************************************************************/
279void destroyWatch(struct watch* w) {
280 /* Stop the loop so the thread will exit */
281 pthread_mutex_lock(&w->mut);
282 FSEventStreamStop(w->eventStream);
283 FSEventStreamInvalidate(w->eventStream);
284 CFRunLoopStop(w->runLoop);
285 CFRelease(w->runLoop);
286 FSEventStreamRelease(w->eventStream);
287 close(w->writefd);
288 pthread_mutex_unlock(&w->mut);
290 /* Cleanup */
291 pthread_mutex_destroy(&w->mut);
292 free(w);
294#endif