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