b6d274675ad94092b6e71c23617cad18511b9f9d
[platform/upstream/nodejs.git] / deps / uv / src / unix / fsevents.c
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:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
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
18  * IN THE SOFTWARE.
19  */
20
21 #include "uv.h"
22 #include "internal.h"
23
24 #if TARGET_OS_IPHONE
25
26 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
27
28 int uv__fsevents_init(uv_fs_event_t* handle) {
29   return 0;
30 }
31
32
33 int uv__fsevents_close(uv_fs_event_t* handle) {
34   return 0;
35 }
36
37 #else /* TARGET_OS_IPHONE */
38
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <CoreServices/CoreServices.h>
42
43 typedef struct uv__fsevents_event_s uv__fsevents_event_t;
44
45 struct uv__fsevents_event_s {
46   int events;
47   ngx_queue_t member;
48   char path[1];
49 };
50
51
52 #define UV__FSEVENTS_WALK(handle, block)                                      \
53     {                                                                         \
54       ngx_queue_t* curr;                                                      \
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);        \
62       }                                                                       \
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)                \
71           block                                                               \
72         /* Free allocated data */                                             \
73         free(event);                                                          \
74       }                                                                       \
75     }
76
77
78 void uv__fsevents_cb(uv_async_t* cb, int status) {
79   uv_fs_event_t* handle;
80
81   handle = cb->data;
82
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);
86   });
87
88   if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 &&
89       handle->event_watcher.fd == -1) {
90     uv__fsevents_close(handle);
91   }
92 }
93
94
95 void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
96                            void* info,
97                            size_t numEvents,
98                            void* eventPaths,
99                            const FSEventStreamEventFlags eventFlags[],
100                            const FSEventStreamEventId eventIds[]) {
101   size_t i;
102   int len;
103   char** paths;
104   char* path;
105   char* pos;
106   uv_fs_event_t* handle;
107   uv__fsevents_event_t* event;
108   ngx_queue_t add_list;
109   int kFSEventsModified;
110   int kFSEventsRenamed;
111
112   kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod |
113                       kFSEventStreamEventFlagItemModified |
114                       kFSEventStreamEventFlagItemInodeMetaMod |
115                       kFSEventStreamEventFlagItemChangeOwner |
116                       kFSEventStreamEventFlagItemXattrMod;
117   kFSEventsRenamed = kFSEventStreamEventFlagItemCreated |
118                      kFSEventStreamEventFlagItemRemoved |
119                      kFSEventStreamEventFlagItemRenamed;
120
121   handle = info;
122   paths = eventPaths;
123   ngx_queue_init(&add_list);
124
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)) {
134       continue;
135     }
136
137     /* TODO: Report errors */
138     path = paths[i];
139     len = strlen(path);
140
141     /* Remove absolute path prefix */
142     if (strstr(path, handle->realpath) == path) {
143       path += handle->realpath_len;
144       len -= handle->realpath_len;
145
146       /* Skip back slash */
147       if (*path != 0) {
148         path++;
149         len--;
150       }
151     }
152
153 #ifdef MAC_OS_X_VERSION_10_7
154     /* Ignore events with path equal to directory itself */
155     if (len == 0)
156       continue;
157 #endif /* MAC_OS_X_VERSION_10_7 */
158
159     /* Do not emit events from subdirectories (without option set) */
160     pos = strchr(path, '/');
161     if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 &&
162         pos != NULL &&
163         pos != path + 1)
164       continue;
165
166 #ifndef MAC_OS_X_VERSION_10_7
167     path = "";
168     len = 0;
169 #endif /* MAC_OS_X_VERSION_10_7 */
170
171     event = malloc(sizeof(*event) + len);
172     if (event == NULL)
173       break;
174
175     memcpy(event->path, path, len + 1);
176
177     if ((eventFlags[i] & kFSEventsModified) != 0 &&
178         (eventFlags[i] & kFSEventsRenamed) == 0)
179       event->events = UV_CHANGE;
180     else
181       event->events = UV_RENAME;
182
183     ngx_queue_insert_tail(&add_list, &event->member);
184   }
185   uv_mutex_lock(&handle->cf_mutex);
186   ngx_queue_add(&handle->cf_events, &add_list);
187   uv_mutex_unlock(&handle->cf_mutex);
188
189   uv_async_send(handle->cf_cb);
190 }
191
192
193 void uv__fsevents_schedule(void* arg) {
194   uv_fs_event_t* handle;
195
196   handle = arg;
197   FSEventStreamScheduleWithRunLoop(handle->cf_eventstream,
198                                    handle->loop->cf_loop,
199                                    kCFRunLoopDefaultMode);
200   FSEventStreamStart(handle->cf_eventstream);
201   uv_sem_post(&handle->cf_sem);
202 }
203
204
205 int uv__fsevents_init(uv_fs_event_t* handle) {
206   FSEventStreamContext ctx;
207   FSEventStreamRef ref;
208   CFStringRef path;
209   CFArrayRef paths;
210   CFAbsoluteTime latency;
211   FSEventStreamCreateFlags flags;
212
213   /* Initialize context */
214   ctx.version = 0;
215   ctx.info = handle;
216   ctx.retain = NULL;
217   ctx.release = NULL;
218   ctx.copyDescription = NULL;
219
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);
224
225   /* Initialize paths array */
226   path = CFStringCreateWithCString(NULL,
227                                    handle->filename,
228                                    CFStringGetSystemEncoding());
229   paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
230
231   latency = 0.15;
232
233   /* Set appropriate flags */
234   flags = kFSEventStreamCreateFlagFileEvents;
235
236   ref = FSEventStreamCreate(NULL,
237                             &uv__fsevents_event_cb,
238                             &ctx,
239                             paths,
240                             kFSEventStreamEventIdSinceNow,
241                             latency,
242                             flags);
243   handle->cf_eventstream = ref;
244
245   /*
246    * Events will occur in other thread.
247    * Initialize callback for getting them back into event loop's thread
248    */
249   handle->cf_cb = malloc(sizeof(*handle->cf_cb));
250   if (handle->cf_cb == NULL)
251     return uv__set_sys_error(handle->loop, ENOMEM);
252
253   handle->cf_cb->data = handle;
254   uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
255   handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
256   uv_unref((uv_handle_t*) handle->cf_cb);
257
258   uv_mutex_init(&handle->cf_mutex);
259   uv_sem_init(&handle->cf_sem, 0);
260   ngx_queue_init(&handle->cf_events);
261
262   uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle);
263
264   return 0;
265 }
266
267
268 int uv__fsevents_close(uv_fs_event_t* handle) {
269   if (handle->cf_eventstream == NULL)
270     return -1;
271
272   /* Ensure that event stream was scheduled */
273   uv_sem_wait(&handle->cf_sem);
274
275   /* Stop emitting events */
276   FSEventStreamStop(handle->cf_eventstream);
277
278   /* Release stream */
279   FSEventStreamInvalidate(handle->cf_eventstream);
280   FSEventStreamRelease(handle->cf_eventstream);
281   handle->cf_eventstream = NULL;
282
283   uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free);
284
285   /* Free data in queue */
286   UV__FSEVENTS_WALK(handle, {
287     /* NOP */
288   })
289
290   uv_mutex_destroy(&handle->cf_mutex);
291   uv_sem_destroy(&handle->cf_sem);
292   free(handle->realpath);
293   handle->realpath = NULL;
294   handle->realpath_len = 0;
295
296   return 0;
297 }
298
299 #endif /* TARGET_OS_IPHONE */