1 /* EIO - EFL data type library
2 * Copyright (C) 2011 Enlightenment Developers:
3 * Cedric Bail <cedric.bail@free.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 /*============================================================================*
24 * Local *
25 *============================================================================*/
26
27 /**
28 * @cond LOCAL
29 */
30
31 static Eina_Hash *_eio_monitors = NULL;
32
33 static void
_eio_monitor_free(Eio_Monitor * monitor)34 _eio_monitor_free(Eio_Monitor *monitor)
35 {
36 if (!monitor->delete_me)
37 eina_hash_del(_eio_monitors, monitor->path, monitor);
38
39 if (monitor->exist)
40 {
41 eio_file_cancel(monitor->exist);
42 monitor->exist = NULL;
43 }
44
45 if (monitor->backend)
46 {
47 if (!monitor->fallback)
48 eio_monitor_backend_del(monitor);
49 else
50 eio_monitor_fallback_del(monitor);
51 }
52
53 if (_eio_log_dom_global != -1)
54 INF("Stopping monitor on '%s'.", monitor->path);
55
56 eina_stringshare_del(monitor->path);
57 free(monitor);
58 }
59
60 static void
_eio_monitor_error_cleanup_cb(EINA_UNUSED void * user_data,void * func_data)61 _eio_monitor_error_cleanup_cb(EINA_UNUSED void *user_data, void *func_data)
62 {
63 Eio_Monitor_Error *ev = func_data;
64
65 EINA_REFCOUNT_UNREF(ev->monitor)
66 _eio_monitor_free(ev->monitor);
67 free(ev);
68 }
69
70 static void
_eio_monitor_event_cleanup_cb(EINA_UNUSED void * user_data,void * func_data)71 _eio_monitor_event_cleanup_cb(EINA_UNUSED void *user_data, void *func_data)
72 {
73 Eio_Monitor_Event *ev = func_data;
74
75 EINA_REFCOUNT_UNREF(ev->monitor)
76 _eio_monitor_free(ev->monitor);
77 eina_stringshare_del(ev->filename);
78 free(ev);
79 }
80
81 static void
_eio_monitor_stat_cb(void * data,EINA_UNUSED Eio_File * handler,EINA_UNUSED const Eina_Stat * st)82 _eio_monitor_stat_cb(void *data, EINA_UNUSED Eio_File *handler, EINA_UNUSED const Eina_Stat *st)
83 {
84 Eio_Monitor *monitor = data;
85
86 monitor->exist = NULL;
87
88 if (EINA_REFCOUNT_GET(monitor) > 1)
89 eio_monitor_backend_add(monitor);
90
91 EINA_REFCOUNT_UNREF(monitor)
92 _eio_monitor_free(monitor);
93 }
94
95 static void
_eio_monitor_error(Eio_Monitor * monitor,int error)96 _eio_monitor_error(Eio_Monitor *monitor, int error)
97 {
98 Eio_Monitor_Error *ev;
99
100 ev = calloc(1, sizeof (Eio_Monitor_Error));
101 if (!ev) return;
102
103 ev->monitor = monitor;
104 EINA_REFCOUNT_REF(ev->monitor);
105 ev->error = error;
106
107 ecore_event_add(EIO_MONITOR_ERROR, ev, _eio_monitor_error_cleanup_cb, NULL);
108 }
109
110 static void
_eio_monitor_error_cb(void * data,Eio_File * handler EINA_UNUSED,int error)111 _eio_monitor_error_cb(void *data, Eio_File *handler EINA_UNUSED, int error)
112 {
113 Eio_Monitor *monitor = data;
114
115 monitor->error = error;
116 monitor->exist = NULL;
117
118 if (EINA_REFCOUNT_GET(monitor) >= 1)
119 _eio_monitor_error(monitor, error);
120
121 EINA_REFCOUNT_UNREF(monitor)
122 _eio_monitor_free(monitor);
123
124 return;
125 }
126
127 /**
128 * @endcond
129 */
130
131 /*============================================================================*
132 * Global *
133 *============================================================================*/
134
135 /**
136 * @cond LOCAL
137 */
138
139 void
eio_monitor_init(void)140 eio_monitor_init(void)
141 {
142 EIO_MONITOR_ERROR = ecore_event_type_new();
143 EIO_MONITOR_SELF_RENAME = ecore_event_type_new();
144 EIO_MONITOR_SELF_DELETED = ecore_event_type_new();
145 EIO_MONITOR_FILE_CREATED = ecore_event_type_new();
146 EIO_MONITOR_FILE_DELETED = ecore_event_type_new();
147 EIO_MONITOR_FILE_MODIFIED = ecore_event_type_new();
148 EIO_MONITOR_FILE_CLOSED = ecore_event_type_new();
149 EIO_MONITOR_DIRECTORY_CREATED = ecore_event_type_new();
150 EIO_MONITOR_DIRECTORY_DELETED = ecore_event_type_new();
151 EIO_MONITOR_DIRECTORY_MODIFIED = ecore_event_type_new();
152 EIO_MONITOR_DIRECTORY_CLOSED = ecore_event_type_new();
153
154 eio_monitor_backend_init();
155 eio_monitor_fallback_init();
156
157 _eio_monitors = eina_hash_stringshared_new(NULL);
158 /* FIXME: this check is optional, but if it is kept then failure should be handled more gracefully */
159 if (!_eio_monitors) abort();
160 }
161
162 void
eio_monitor_shutdown(void)163 eio_monitor_shutdown(void)
164 {
165 Eina_Iterator *it;
166 Eio_Monitor *monitor;
167
168 ecore_event_type_flush(EIO_MONITOR_ERROR,
169 EIO_MONITOR_SELF_RENAME,
170 EIO_MONITOR_SELF_DELETED,
171 EIO_MONITOR_FILE_CREATED,
172 EIO_MONITOR_FILE_DELETED,
173 EIO_MONITOR_FILE_MODIFIED,
174 EIO_MONITOR_FILE_CLOSED,
175 EIO_MONITOR_DIRECTORY_CREATED,
176 EIO_MONITOR_DIRECTORY_DELETED,
177 EIO_MONITOR_DIRECTORY_MODIFIED,
178 EIO_MONITOR_DIRECTORY_CLOSED);
179
180 it = eina_hash_iterator_data_new(_eio_monitors);
181 EINA_ITERATOR_FOREACH(it, monitor)
182 {
183 if (monitor->exist)
184 {
185 eio_file_cancel(monitor->exist);
186 monitor->exist = NULL;
187 }
188 monitor->delete_me = EINA_TRUE;
189 }
190 eina_iterator_free(it);
191 eina_hash_free(_eio_monitors);
192 _eio_monitors = NULL;
193
194 eio_monitor_backend_shutdown();
195 eio_monitor_fallback_shutdown();
196 }
197
198 static const char *
_eio_naming_event(int event_code)199 _eio_naming_event(int event_code)
200 {
201 #define EVENT_CHECK(Code, Ev) if (Code == Ev) return #Ev;
202
203 EVENT_CHECK(event_code, EIO_MONITOR_ERROR);
204 EVENT_CHECK(event_code, EIO_MONITOR_FILE_CREATED);
205 EVENT_CHECK(event_code, EIO_MONITOR_FILE_DELETED);
206 EVENT_CHECK(event_code, EIO_MONITOR_FILE_MODIFIED);
207 EVENT_CHECK(event_code, EIO_MONITOR_FILE_CLOSED);
208 EVENT_CHECK(event_code, EIO_MONITOR_DIRECTORY_CREATED);
209 EVENT_CHECK(event_code, EIO_MONITOR_DIRECTORY_DELETED);
210 EVENT_CHECK(event_code, EIO_MONITOR_DIRECTORY_MODIFIED);
211 EVENT_CHECK(event_code, EIO_MONITOR_DIRECTORY_CLOSED);
212 EVENT_CHECK(event_code, EIO_MONITOR_SELF_RENAME);
213 EVENT_CHECK(event_code, EIO_MONITOR_SELF_DELETED);
214 return "Unknown";
215 }
216
217 void
_eio_monitor_send(Eio_Monitor * monitor,const char * filename,int event_code)218 _eio_monitor_send(Eio_Monitor *monitor, const char *filename, int event_code)
219 {
220 Eio_Monitor_Event *ev;
221
222 if (monitor->delete_me)
223 return;
224
225 INF("Event '%s' for monitored path '%s'.",
226 _eio_naming_event(event_code), filename);
227
228 ev = calloc(1, sizeof (Eio_Monitor_Event));
229 if (!ev) return;
230
231 ev->monitor = monitor;
232 EINA_REFCOUNT_REF(ev->monitor);
233 ev->filename = eina_stringshare_add(filename);
234
235 ecore_event_add(event_code, ev, _eio_monitor_event_cleanup_cb, NULL);
236 }
237
238 void
_eio_monitor_rename(Eio_Monitor * monitor,const char * newpath)239 _eio_monitor_rename(Eio_Monitor *monitor, const char *newpath)
240 {
241 const char *tmp;
242
243 if (monitor->delete_me)
244 return;
245
246 /* destroy old state */
247 if (monitor->exist)
248 {
249 eio_file_cancel(monitor->exist);
250 monitor->exist = NULL;
251 }
252
253 if (monitor->backend)
254 {
255 if (!monitor->fallback)
256 eio_monitor_backend_del(monitor);
257 else
258 eio_monitor_fallback_del(monitor);
259 }
260
261 INF("Renaming path '%s' to '%s'.",
262 monitor->path, newpath);
263
264 /* rename */
265 tmp = monitor->path;
266 monitor->path = eina_stringshare_add(newpath);
267 eina_hash_move(_eio_monitors, tmp, monitor->path);
268 eina_stringshare_del(tmp);
269
270 /* That means death (cmp pointer and not content) */
271 /* this - i think, is wrong. if the paths are the same, we should just
272 * re-stat anyway. imagine the file was renamed and then replaced?
273 * disable this as this was part of a possible crash due to eio.
274 if (tmp == monitor->path)
275 {
276 _eio_monitor_error(monitor, -1);
277 return;
278 }
279 */
280
281 EINA_REFCOUNT_REF(monitor); /* as we spawn a thread for this monitor, we need to refcount specifically for it */
282
283 /* restart */
284 monitor->rename = EINA_TRUE;
285 monitor->exist = eio_file_direct_stat(monitor->path,
286 _eio_monitor_stat_cb,
287 _eio_monitor_error_cb,
288 monitor);
289
290 /* FIXME: probably should handle this more gracefully */
291 if (!monitor->exist) abort();
292 /* and notify the app */
293 _eio_monitor_send(monitor, newpath, EIO_MONITOR_SELF_RENAME);
294 }
295
296 /**
297 * @endcond
298 */
299
300
301 /*============================================================================*
302 * API *
303 *============================================================================*/
304
305 EAPI int EIO_MONITOR_ERROR;
306 EAPI int EIO_MONITOR_FILE_CREATED;
307 EAPI int EIO_MONITOR_FILE_DELETED;
308 EAPI int EIO_MONITOR_FILE_MODIFIED;
309 EAPI int EIO_MONITOR_FILE_CLOSED;
310 EAPI int EIO_MONITOR_DIRECTORY_CREATED;
311 EAPI int EIO_MONITOR_DIRECTORY_DELETED;
312 EAPI int EIO_MONITOR_DIRECTORY_MODIFIED;
313 EAPI int EIO_MONITOR_DIRECTORY_CLOSED;
314 EAPI int EIO_MONITOR_SELF_RENAME;
315 EAPI int EIO_MONITOR_SELF_DELETED;
316
317 EAPI Eio_Monitor *
eio_monitor_add(const char * path)318 eio_monitor_add(const char *path)
319 {
320 const char *tmp;
321 Eio_Monitor *ret;
322
323 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
324 tmp = eina_stringshare_add(path);
325 ret = eio_monitor_stringshared_add(tmp);
326 eina_stringshare_del(tmp);
327 return ret;
328 }
329
330 EAPI Eio_Monitor *
eio_monitor_stringshared_add(const char * path)331 eio_monitor_stringshared_add(const char *path)
332 {
333 Eio_Monitor *monitor;
334 struct stat st;
335
336 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
337 EINA_SAFETY_ON_NULL_RETURN_VAL(_eio_monitors, NULL);
338
339 if (stat(path, &st) != 0)
340 {
341 ERR("monitored path '%s' not found.", path);
342 return NULL;
343 }
344
345 monitor = eina_hash_find(_eio_monitors, path);
346
347 if (monitor)
348 {
349 if (st.st_mtime != monitor->mtime)
350 {
351 monitor->delete_me = EINA_TRUE;
352 eina_hash_del(_eio_monitors, monitor->path, monitor);
353 }
354 else
355 {
356 EINA_REFCOUNT_REF(monitor);
357 return monitor;
358 }
359 }
360
361 monitor = malloc(sizeof (Eio_Monitor));
362 if (!monitor) return NULL;
363
364 monitor->mtime = st.st_mtime;
365 monitor->backend = NULL; // This is needed to avoid race condition
366 monitor->path = eina_stringshare_ref(path);
367 monitor->fallback = EINA_FALSE;
368 monitor->rename = EINA_FALSE;
369 monitor->delete_me = EINA_FALSE;
370 monitor->exist = NULL;
371
372 EINA_REFCOUNT_INIT(monitor);
373
374 static signed char monpoll = -1;
375
376 if (monpoll == -1)
377 {
378 if (getenv("EIO_MONITOR_POLL")) monpoll = 1;
379 else monpoll = 0;
380 }
381 if (monpoll)
382 eio_monitor_fallback_add(monitor);
383 else
384 eio_monitor_backend_add(monitor);
385
386 if (!monitor->backend)
387 {
388 WRN("Impossible to create a monitor for '%s'.", monitor->path);
389 eina_stringshare_del(monitor->path);
390 free(monitor);
391 return NULL;
392 }
393
394 eina_hash_direct_add(_eio_monitors, path, monitor);
395 INF("New monitor on '%s'.", path);
396
397 return monitor;
398 }
399
400 EAPI void
eio_monitor_del(Eio_Monitor * monitor)401 eio_monitor_del(Eio_Monitor *monitor)
402 {
403 if (!monitor) return;
404 EINA_REFCOUNT_UNREF(monitor)
405 _eio_monitor_free(monitor);
406 }
407
408 EAPI const char *
eio_monitor_path_get(Eio_Monitor * monitor)409 eio_monitor_path_get(Eio_Monitor *monitor)
410 {
411 EINA_SAFETY_ON_NULL_RETURN_VAL(monitor, NULL);
412 return monitor->path;
413 }
414
415
416 EAPI Eina_Bool
eio_monitor_has_context(const Eio_Monitor * monitor,const char * path)417 eio_monitor_has_context(const Eio_Monitor *monitor, const char *path)
418 {
419 if (monitor->fallback)
420 {
421 return eio_monitor_fallback_context_check(monitor, path);
422 }
423 else
424 {
425 return eio_monitor_context_check(monitor, path);
426 }
427 }
428