1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 * Permission is hereby granted, free of charge, to any person obtaining a copy
3 * of this software and associated documentation files (the "Software"), to
4 * deal in the Software without restriction, including without limitation the
5 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6 * sell copies of the Software, and to permit persons to whom the Software is
7 * furnished to do so, subject to the following conditions:
9 * The above copyright notice and this permission notice shall be included in
10 * all copies or substantial portions of the Software.
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
28 int uv__fsevents_init(uv_fs_event_t* handle) {
33 int uv__fsevents_close(uv_fs_event_t* handle) {
37 #else /* TARGET_OS_IPHONE */
41 #include <CoreServices/CoreServices.h>
43 typedef struct uv__fsevents_event_s uv__fsevents_event_t;
45 struct uv__fsevents_event_s {
52 #define UV__FSEVENTS_WALK(handle, block) \
55 ngx_queue_t split_head; \
56 uv__fsevents_event_t* event; \
57 uv_mutex_lock(&(handle)->cf_mutex); \
58 ngx_queue_init(&split_head); \
59 if (!ngx_queue_empty(&(handle)->cf_events)) { \
60 ngx_queue_t* split_pos = ngx_queue_next(&(handle)->cf_events); \
61 ngx_queue_split(&(handle)->cf_events, split_pos, &split_head); \
63 uv_mutex_unlock(&(handle)->cf_mutex); \
64 while (!ngx_queue_empty(&split_head)) { \
65 curr = ngx_queue_head(&split_head); \
66 /* Invoke callback */ \
67 event = ngx_queue_data(curr, uv__fsevents_event_t, member); \
68 ngx_queue_remove(curr); \
69 /* Invoke block code, but only if handle wasn't closed */ \
70 if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0) \
72 /* Free allocated data */ \
78 void uv__fsevents_cb(uv_async_t* cb, int status) {
79 uv_fs_event_t* handle;
83 UV__FSEVENTS_WALK(handle, {
84 if (handle->event_watcher.fd != -1)
85 handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
88 if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 &&
89 handle->event_watcher.fd == -1) {
90 uv__fsevents_close(handle);
95 void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
99 const FSEventStreamEventFlags eventFlags[],
100 const FSEventStreamEventId eventIds[]) {
106 uv_fs_event_t* handle;
107 uv__fsevents_event_t* event;
108 ngx_queue_t add_list;
109 int kFSEventsModified;
110 int kFSEventsRenamed;
112 kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod |
113 kFSEventStreamEventFlagItemModified |
114 kFSEventStreamEventFlagItemInodeMetaMod |
115 kFSEventStreamEventFlagItemChangeOwner |
116 kFSEventStreamEventFlagItemXattrMod;
117 kFSEventsRenamed = kFSEventStreamEventFlagItemCreated |
118 kFSEventStreamEventFlagItemRemoved |
119 kFSEventStreamEventFlagItemRenamed;
123 ngx_queue_init(&add_list);
125 for (i = 0; i < numEvents; i++) {
126 /* Ignore system events */
127 if (eventFlags[i] & (kFSEventStreamEventFlagUserDropped |
128 kFSEventStreamEventFlagKernelDropped |
129 kFSEventStreamEventFlagEventIdsWrapped |
130 kFSEventStreamEventFlagHistoryDone |
131 kFSEventStreamEventFlagMount |
132 kFSEventStreamEventFlagUnmount |
133 kFSEventStreamEventFlagRootChanged)) {
137 /* TODO: Report errors */
141 /* Remove absolute path prefix */
142 if (strstr(path, handle->realpath) == path) {
143 path += handle->realpath_len;
144 len -= handle->realpath_len;
146 /* Skip back slash */
153 #ifdef MAC_OS_X_VERSION_10_7
154 /* Ignore events with path equal to directory itself */
157 #endif /* MAC_OS_X_VERSION_10_7 */
159 /* Do not emit events from subdirectories (without option set) */
160 pos = strchr(path, '/');
161 if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 &&
166 #ifndef MAC_OS_X_VERSION_10_7
169 #endif /* MAC_OS_X_VERSION_10_7 */
171 event = malloc(sizeof(*event) + len);
175 memcpy(event->path, path, len + 1);
177 if ((eventFlags[i] & kFSEventsModified) != 0 &&
178 (eventFlags[i] & kFSEventsRenamed) == 0)
179 event->events = UV_CHANGE;
181 event->events = UV_RENAME;
183 ngx_queue_insert_tail(&add_list, &event->member);
185 uv_mutex_lock(&handle->cf_mutex);
186 ngx_queue_add(&handle->cf_events, &add_list);
187 uv_mutex_unlock(&handle->cf_mutex);
189 uv_async_send(handle->cf_cb);
193 void uv__fsevents_schedule(void* arg) {
194 uv_fs_event_t* handle;
197 FSEventStreamScheduleWithRunLoop(handle->cf_eventstream,
198 handle->loop->cf_loop,
199 kCFRunLoopDefaultMode);
200 FSEventStreamStart(handle->cf_eventstream);
201 uv_sem_post(&handle->cf_sem);
205 int uv__fsevents_init(uv_fs_event_t* handle) {
206 FSEventStreamContext ctx;
207 FSEventStreamRef ref;
210 CFAbsoluteTime latency;
211 FSEventStreamCreateFlags flags;
213 /* Initialize context */
218 ctx.copyDescription = NULL;
220 /* Get absolute path to file */
221 handle->realpath = realpath(handle->filename, NULL);
222 if (handle->realpath != NULL)
223 handle->realpath_len = strlen(handle->realpath);
225 /* Initialize paths array */
226 path = CFStringCreateWithFileSystemRepresentation(NULL, handle->filename);
227 paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
231 /* Set appropriate flags */
232 flags = kFSEventStreamCreateFlagFileEvents;
234 ref = FSEventStreamCreate(NULL,
235 &uv__fsevents_event_cb,
238 kFSEventStreamEventIdSinceNow,
241 handle->cf_eventstream = ref;
244 * Events will occur in other thread.
245 * Initialize callback for getting them back into event loop's thread
247 handle->cf_cb = malloc(sizeof(*handle->cf_cb));
248 if (handle->cf_cb == NULL)
249 return uv__set_sys_error(handle->loop, ENOMEM);
251 handle->cf_cb->data = handle;
252 uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
253 handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
254 uv_unref((uv_handle_t*) handle->cf_cb);
256 uv_mutex_init(&handle->cf_mutex);
257 uv_sem_init(&handle->cf_sem, 0);
258 ngx_queue_init(&handle->cf_events);
260 uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle);
266 int uv__fsevents_close(uv_fs_event_t* handle) {
267 if (handle->cf_eventstream == NULL)
270 /* Ensure that event stream was scheduled */
271 uv_sem_wait(&handle->cf_sem);
273 /* Stop emitting events */
274 FSEventStreamStop(handle->cf_eventstream);
277 FSEventStreamInvalidate(handle->cf_eventstream);
278 FSEventStreamRelease(handle->cf_eventstream);
279 handle->cf_eventstream = NULL;
281 uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free);
283 /* Free data in queue */
284 UV__FSEVENTS_WALK(handle, {
288 uv_mutex_destroy(&handle->cf_mutex);
289 uv_sem_destroy(&handle->cf_sem);
290 free(handle->realpath);
291 handle->realpath = NULL;
292 handle->realpath_len = 0;
297 #endif /* TARGET_OS_IPHONE */