summaryrefslogtreecommitdiff
path: root/src/lib/eio/eio_monitor_cocoa.c
diff options
context:
space:
mode:
authorpierre lamot <pierre.lamot@openwide.fr>2015-02-26 14:37:45 +0100
committerCedric BAIL <cedric@osg.samsung.com>2015-03-12 07:43:59 +0100
commitd958afd9fd7078a6eaee17df0a0b1e71504fc3b1 (patch)
treef514b4f3cb5d7cec654371fc6f029800695e4648 /src/lib/eio/eio_monitor_cocoa.c
parentae4389a7bd8a6807c3df041cde7c1357b0c13a0c (diff)
eio: add OSX implementation of eio_monitor based on FSEvent
this patch adds an implementation of eio_monitor based on FSEvent for OSX. This implentation has some limitations compared to inotify implementation. Folowing events are not detected: - EIO_MONITOR_FILE_CLOSED - EIO_MONITOR_SELF_RENAME - EIO_MONITOR_SELF_DELETED It should be noted that some events that happend before the call to eio_monitor_add can be catched. This is why sleep timers have been added in the test suite. Tests have been added to check uncovered scenarios. some things might still be improved: - self_deleted events for files might be handled by checking the file_name manually - self_deleted events for directories might be handled by setting kFSEventStreamCreateFlagWatchRoot. I've noticed by doing so that a lot more unwanted event are raised Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
Diffstat (limited to 'src/lib/eio/eio_monitor_cocoa.c')
-rw-r--r--src/lib/eio/eio_monitor_cocoa.c405
1 files changed, 405 insertions, 0 deletions
diff --git a/src/lib/eio/eio_monitor_cocoa.c b/src/lib/eio/eio_monitor_cocoa.c
new file mode 100644
index 0000000..ea1d793
--- /dev/null
+++ b/src/lib/eio/eio_monitor_cocoa.c
@@ -0,0 +1,405 @@
1/* EIO - EFL data type library
2 * Copyright (C) 2015 Enlightenment Developers:
3 * Pierre Lamot <pierre.lamot@openwide.fr>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library;
17 * if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "eio_private.h"
21#include "Eio.h"
22
23#import <CoreServices/CoreServices.h>
24
25static CFTimeInterval _latency = 0.1;
26
27/*============================================================================*
28 * Local *
29 *============================================================================*/
30
31/**
32 * @cond LOCAL
33 */
34
35struct _Eio_Monitor_Backend
36{
37 Eio_Monitor *parent;
38 ///the monitored path
39 char *mon_path;
40 ///the actual file path
41 char *real_path;
42};
43
44typedef struct _FSEvent_Info FSEvent_Info;
45
46struct _FSEvent_Info {
47 char *path;
48 FSEventStreamEventFlags flags;
49
50};
51
52typedef struct _Eio_FSEvent_Table Eio_FSEvent_Table;
53
54struct _Eio_FSEvent_Table
55{
56 int mask;
57 int *ev_file_code;
58 int *ev_dir_code;
59};
60
61#define EIO_FSEVENT_LINE(FSe, Ef, Ed) \
62 { kFSEventStreamEventFlag##FSe, &EIO_MONITOR_##Ef, &EIO_MONITOR_##Ed }
63
64static const Eio_FSEvent_Table match[] = {
65 EIO_FSEVENT_LINE(ItemChangeOwner, FILE_MODIFIED, DIRECTORY_MODIFIED),
66 EIO_FSEVENT_LINE(ItemInodeMetaMod, FILE_MODIFIED, DIRECTORY_MODIFIED),
67 EIO_FSEVENT_LINE(ItemXattrMod, FILE_MODIFIED, DIRECTORY_MODIFIED),
68 EIO_FSEVENT_LINE(ItemModified, FILE_MODIFIED, DIRECTORY_MODIFIED),
69 EIO_FSEVENT_LINE(ItemRemoved, FILE_DELETED, DIRECTORY_DELETED),
70 EIO_FSEVENT_LINE(ItemCreated, FILE_CREATED, DIRECTORY_CREATED),
71 EIO_FSEVENT_LINE(RootChanged, SELF_DELETED, SELF_DELETED)
72};
73
74static FSEventStreamRef _stream = NULL;
75static Eina_Hash *_fsevent_monitors = NULL;
76static CFMutableArrayRef _paths_to_watch = NULL;
77static dispatch_queue_t _dispatch_queue;
78
79static Eina_Bool
80_handle_fsevent_with_monitor(const Eina_Hash *hash EINA_UNUSED,
81 const void *key EINA_UNUSED,
82 void *data,
83 void *fdata)
84{
85 FSEvent_Info *event_info = (FSEvent_Info*)fdata;
86 Eio_Monitor_Backend *backend = (Eio_Monitor_Backend*)data;
87 FSEventStreamEventFlags flags = event_info->flags;
88 unsigned int i;
89 Eina_Bool is_dir;
90 unsigned int length, tmp_length;
91
92 char *tmp = NULL;
93
94 if (backend->parent->delete_me)
95 return 1;
96
97 if (!eina_str_has_prefix(event_info->path, backend->real_path))
98 {
99 return 1;
100 }
101
102 length = strlen(event_info->path) - strlen(backend->real_path);
103 if (length == 0)
104 {
105 tmp = strdup(backend->parent->path);
106 }
107 else
108 {
109 tmp_length =
110 eina_stringshare_strlen(backend->parent->path) + length + 2;
111
112 tmp = malloc(sizeof(char) * tmp_length);
113 snprintf(tmp, tmp_length, "%s/%s",
114 backend->parent->path,
115 &(event_info->path[strlen(backend->real_path) + 1]));
116 }
117
118 is_dir = !!(flags & kFSEventStreamEventFlagItemIsDir);
119
120 for (i = 0; i < sizeof (match) / sizeof (Eio_FSEvent_Table); ++i)
121 if (match[i].mask & flags)
122 {
123 DBG("send event from %s with event %X\n", event_info->path, flags);
124 _eio_monitor_send(backend->parent,
125 tmp,
126 is_dir ? *match[i].ev_dir_code : *match[i].ev_file_code);
127 }
128
129 free(tmp);
130 //we have found the right event, no need to continue
131 return 0;
132}
133
134
135static void
136_main_loop_send_event(void *data)
137{
138 FSEvent_Info *info = (FSEvent_Info*)data;
139
140 if (!_stream)
141 {
142 //this can happen, when eio_shutdown is called
143 goto cleanup;
144 }
145
146 if ((info->flags & kFSEventStreamEventFlagKernelDropped) != 0)
147 {
148 _eio_monitor_send(NULL, "", EIO_MONITOR_ERROR);
149 goto cleanup;
150 }
151
152 eina_hash_foreach(_fsevent_monitors,
153 _handle_fsevent_with_monitor,
154 info);
155
156 cleanup:
157 free(info->path);
158 free(info);
159}
160
161
162static void
163_eio_fsevent_cb(ConstFSEventStreamRef stream_ref EINA_UNUSED,
164 void *ctx EINA_UNUSED,
165 size_t count,
166 void *event_paths,
167 const FSEventStreamEventFlags event_flags[],
168 const FSEventStreamEventId event_ids[] EINA_UNUSED
169 )
170{
171 size_t i;
172 FSEvent_Info *event_info;
173
174 for (i = 0; i < count; i++)
175 {
176 event_info = malloc(sizeof(FSEvent_Info));
177 event_info->path = strdup(((char**)event_paths)[i]);
178 event_info->flags = event_flags[i];
179
180 ecore_main_loop_thread_safe_call_async(_main_loop_send_event,
181 event_info);
182 }
183}
184
185static void
186_eio_fsevent_del(void *data)
187{
188 Eio_Monitor_Backend *backend = (Eio_Monitor_Backend *)data;
189 free(backend);
190}
191
192static void
193_eio_get_monitor_path(const char *path, char **monpath, char **fullpath)
194{
195 char realPath[PATH_MAX];
196 char *realPathOk;
197 char *dname = NULL;
198 char *fname = NULL;
199 struct stat sb;
200
201 realPathOk = realpath(path, realPath);
202 if (realPathOk == NULL)
203 {
204 dname = dirname((char*)path);
205 if (strcmp(dname, ".") == 0)
206 {
207 realPathOk = realpath("./", realPath);
208 }
209 else
210 {
211 realPathOk = realpath(dname, realPath);
212 }
213
214 if (realPathOk == NULL)
215 return;
216 }
217
218 if (stat(realPath, &sb) < 0)
219 {
220 return;
221 }
222
223 if (S_ISDIR(sb.st_mode))
224 {
225 if (fullpath)
226 *fullpath = strdup(realPath);
227 if (monpath)
228 *monpath = strdup(realPath);
229 }
230 else
231 {
232 //not a directory, monitor parent
233 if (fullpath)
234 *fullpath = strdup(realPath);
235 dname = dirname(realPath);
236 if (monpath)
237 *monpath = strdup(dname);
238 }
239}
240
241/**
242 * @endcond
243 */
244
245
246/*============================================================================*
247 * Global *
248 *============================================================================*/
249
250/**
251 * @cond LOCAL
252 */
253
254/**
255 * @endcond
256 */
257
258void eio_monitor_backend_init(void)
259{
260 _dispatch_queue = dispatch_queue_create("org.elf.fseventqueue", NULL);
261 _fsevent_monitors = eina_hash_string_small_new(_eio_fsevent_del);
262 _paths_to_watch = CFArrayCreateMutable(kCFAllocatorDefault,
263 0,
264 &kCFTypeArrayCallBacks);
265}
266
267void eio_monitor_backend_shutdown(void)
268{
269 if (_stream)
270 {
271 FSEventStreamStop(_stream);
272 FSEventStreamInvalidate(_stream);
273 FSEventStreamRelease(_stream);
274 _stream = NULL;
275 }
276 dispatch_release(_dispatch_queue);
277 eina_hash_free(_fsevent_monitors);
278 CFRelease(_paths_to_watch);
279}
280
281
282void eio_monitor_backend_add(Eio_Monitor *monitor)
283{
284 Eio_Monitor_Backend *backend;
285
286 FSEventStreamEventId eventid;
287
288 CFStringRef path = NULL;
289
290 //the path we should monitor
291 char *monitor_path = NULL;
292 //the real file path
293 char *real_path = NULL;
294
295 _eio_get_monitor_path(monitor->path, &monitor_path, &real_path);
296
297 backend = calloc(1, sizeof (Eio_Monitor_Backend));
298 if (!backend)
299 {
300 free(monitor_path);
301 eio_monitor_fallback_add(monitor);
302 return;
303 }
304
305 path = CFStringCreateWithCString(NULL,
306 monitor_path,
307 kCFStringEncodingUTF8);
308
309 CFArrayAppendValue(_paths_to_watch, path);
310
311 if (_stream)
312 {
313 eventid = FSEventStreamGetLatestEventId(_stream);
314 FSEventStreamRelease(_stream);
315 _stream = NULL;
316 }
317 else
318 {
319 eventid = kFSEventStreamEventIdSinceNow;
320 }
321
322 _stream = FSEventStreamCreate(NULL,
323 _eio_fsevent_cb,
324 NULL,
325 _paths_to_watch,
326 eventid,
327 _latency,
328 kFSEventStreamCreateFlagFileEvents
329 | kFSEventStreamCreateFlagNoDefer
330 );
331
332 if (!_stream)
333 {
334 eio_monitor_fallback_add(monitor);
335 return;
336 }
337
338 backend->parent = monitor;
339 backend->mon_path = monitor_path;
340 backend->real_path = real_path;
341 monitor->backend = backend;
342
343 eina_hash_direct_add(_fsevent_monitors, monitor->path, backend);
344
345 FSEventStreamSetDispatchQueue(_stream, _dispatch_queue);
346 FSEventStreamStart(_stream);
347
348
349}
350
351void eio_monitor_backend_del(Eio_Monitor *monitor)
352{
353 Eio_Monitor_Backend *backend;
354 CFStringRef path = NULL;
355 FSEventStreamEventId eventid;
356 char *monitor_path;
357
358 if (!_stream)
359 {
360 eio_monitor_fallback_del(monitor);
361 return;
362 }
363
364 _eio_get_monitor_path(monitor->path, &monitor_path, NULL);
365
366 eventid = FSEventStreamGetLatestEventId(_stream);
367 FSEventStreamRelease(_stream);
368 _stream = NULL;
369
370 path = CFStringCreateWithCString(NULL,
371 monitor_path,
372 kCFStringEncodingUTF8);
373
374 CFIndex idx =
375 CFArrayGetFirstIndexOfValue(_paths_to_watch,
376 CFRangeMake(0,
377 CFArrayGetCount(_paths_to_watch)
378 ),
379 path);
380
381 if (idx != -1)
382 {
383 CFArrayRemoveValueAtIndex(_paths_to_watch, idx);
384 }
385
386 _stream = FSEventStreamCreate(NULL,
387 _eio_fsevent_cb,
388 NULL,
389 _paths_to_watch,
390 eventid,
391 _latency,
392 kFSEventStreamCreateFlagFileEvents
393 | kFSEventStreamCreateFlagNoDefer
394 );
395 backend = monitor->backend;
396 monitor->backend = NULL;
397 if (!backend) return;
398
399 eina_hash_del(_fsevent_monitors, monitor->path, backend);
400}
401
402
403/*============================================================================*
404 * API *
405 *============================================================================*/