1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <errno.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 
13 #include "ecore_file_private.h"
14 
15 /*
16  * TODO:
17  *
18  * - Listen to these events:
19  *   IN_ACCESS, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, IN_OPEN
20  * - Read all events first, then call the callbacks. This will prevent several
21  *   callbacks with the typic save cycle (delete file, new file)
22  * - Listen to IN_IGNORED, emitted when the watch is removed
23  */
24 
25 #include <sys/inotify.h>
26 
27 
28 typedef struct _Ecore_File_Monitor_Inotify Ecore_File_Monitor_Inotify;
29 
30 #define ECORE_FILE_MONITOR_INOTIFY(x) ((Ecore_File_Monitor_Inotify *)(x))
31 
32 struct _Ecore_File_Monitor_Inotify
33 {
34    Ecore_File_Monitor  monitor;
35    int                 wd;
36 };
37 
38 static Ecore_Fd_Handler *_fdh = NULL;
39 static Ecore_File_Monitor    *_monitors = NULL;
40 static pid_t             _inotify_fd_pid = -1;
41 
42 static Eina_Bool           _ecore_file_monitor_inotify_handler(void *data, Ecore_Fd_Handler *fdh);
43 static Eina_List *_ecore_file_monitor_inotify_monitor_find(int wd);
44 static void                _ecore_file_monitor_inotify_events(Ecore_File_Monitor *em, char *file, int mask);
45 static int                 _ecore_file_monitor_inotify_monitor(Ecore_File_Monitor *em, const char *path);
46 #if 0
47 static void                _ecore_file_monitor_inotify_print(char *file, int mask);
48 #endif
49 
50 static Eina_Bool reseting;
51 static Eina_Hash *monitor_hash;
52 
53 static void
_ecore_file_monitor_inotify_reset()54 _ecore_file_monitor_inotify_reset()
55 {
56    Eina_Iterator *it;
57    Ecore_File_Monitor *em;
58    Eina_Hash *h = monitor_hash;
59    monitor_hash = NULL;
60    reseting = 1;
61    ecore_file_monitor_backend_shutdown();
62    ecore_file_monitor_backend_init();
63    it = eina_hash_iterator_data_new(h);
64    EINA_ITERATOR_FOREACH(it, em)
65      _ecore_file_monitor_inotify_monitor(em, em->path);
66    eina_iterator_free(it);
67    eina_hash_free(h);
68    reseting = 0;
69 }
70 
71 int
ecore_file_monitor_backend_init(void)72 ecore_file_monitor_backend_init(void)
73 {
74    int fd;
75 
76    fd = inotify_init();
77    if (fd < 0)
78      return 0;
79 
80    eina_file_close_on_exec(fd, EINA_TRUE);
81 
82    _fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ, _ecore_file_monitor_inotify_handler,
83                                     NULL, NULL, NULL);
84    if (!_fdh)
85      {
86         close(fd);
87         return 0;
88      }
89 
90    if (!reseting)
91      ecore_fork_reset_callback_add(_ecore_file_monitor_inotify_reset, NULL);
92    _inotify_fd_pid = getpid();
93    monitor_hash = eina_hash_int32_new(NULL);
94    return 1;
95 }
96 
97 int
ecore_file_monitor_backend_shutdown(void)98 ecore_file_monitor_backend_shutdown(void)
99 {
100    int fd;
101 
102    while(_monitors)
103         ecore_file_monitor_backend_del(_monitors);
104 
105    if (_fdh)
106      {
107         fd = ecore_main_fd_handler_fd_get(_fdh);
108         ecore_main_fd_handler_del(_fdh);
109         if (fd > -1)
110           close(fd);
111         _fdh = NULL;
112      }
113    eina_hash_free(monitor_hash);
114    monitor_hash = NULL;
115    _inotify_fd_pid = -1;
116    if (!reseting)
117      ecore_fork_reset_callback_del(_ecore_file_monitor_inotify_reset, NULL);
118    return 1;
119 }
120 
121 Ecore_File_Monitor *
ecore_file_monitor_backend_add(const char * path,void (* func)(void * data,Ecore_File_Monitor * em,Ecore_File_Event event,const char * path),void * data)122 ecore_file_monitor_backend_add(const char *path,
123                                void (*func) (void *data, Ecore_File_Monitor *em,
124                                              Ecore_File_Event event,
125                                              const char *path),
126                                void *data)
127 {
128    Ecore_File_Monitor *em;
129    char *path2;
130    size_t len;
131 
132    if (_inotify_fd_pid == -1) return NULL;
133 
134    if (_inotify_fd_pid != getpid())
135      _ecore_file_monitor_inotify_reset();
136 
137    em = (Ecore_File_Monitor *)calloc(1, sizeof(Ecore_File_Monitor_Inotify));
138    if (!em) return NULL;
139 
140    em->func = func;
141    em->data = data;
142 
143    len = strlen(path);
144    path2 = alloca(len + 1);
145    strcpy(path2, path);
146    if (path2[len - 1] == '/' && strcmp(path2, "/")) path2[len - 1] = 0;
147    em->path = eina_stringshare_add(path2);
148 
149    _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
150 
151    if (!_ecore_file_monitor_inotify_monitor(em, em->path))
152      return NULL;
153 
154    return em;
155 }
156 
157 void
ecore_file_monitor_backend_del(Ecore_File_Monitor * em)158 ecore_file_monitor_backend_del(Ecore_File_Monitor *em)
159 {
160    int fd;
161 
162    if (_monitors)
163      _monitors = ECORE_FILE_MONITOR(eina_inlist_remove(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
164    if (ECORE_FILE_MONITOR_INOTIFY(em)->wd >= 0)
165      eina_hash_list_remove(monitor_hash, &ECORE_FILE_MONITOR_INOTIFY(em)->wd, em);
166 
167    fd = ecore_main_fd_handler_fd_get(_fdh);
168    if (ECORE_FILE_MONITOR_INOTIFY(em)->wd >= 0)
169      inotify_rm_watch(fd, ECORE_FILE_MONITOR_INOTIFY(em)->wd);
170    eina_stringshare_del(em->path);
171    free(em);
172 }
173 
174 static Eina_Bool
_ecore_file_monitor_inotify_handler(void * data EINA_UNUSED,Ecore_Fd_Handler * fdh)175 _ecore_file_monitor_inotify_handler(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh)
176 {
177    Eina_List *l, *ll, *ll2;
178    Ecore_File_Monitor *em;
179    char buffer[16384];
180    struct inotify_event *event;
181    int i = 0, fd;
182    int event_size;
183    ssize_t size;
184 
185    fd = ecore_main_fd_handler_fd_get(fdh);
186    if (fd < 0) return ECORE_CALLBACK_RENEW;
187 
188    size = read(fd, buffer, sizeof(buffer));
189    while ((i + (int) sizeof(struct inotify_event)) <= (int) size)
190      {
191         event = (struct inotify_event *)&buffer[i];
192         event_size = sizeof(struct inotify_event) + event->len;
193         if ((event_size + i) > size) break ;
194         i += event_size;
195 
196         l = _ecore_file_monitor_inotify_monitor_find(event->wd);
197         EINA_LIST_FOREACH_SAFE(l, ll, ll2, em)
198           _ecore_file_monitor_inotify_events(em, (event->len ? event->name : NULL), event->mask);
199      }
200 
201    return ECORE_CALLBACK_RENEW;
202 }
203 
204 static Eina_List *
_ecore_file_monitor_inotify_monitor_find(int wd)205 _ecore_file_monitor_inotify_monitor_find(int wd)
206 {
207    return eina_hash_find(monitor_hash, &wd);
208 }
209 
210 static void
_ecore_file_monitor_inotify_events(Ecore_File_Monitor * em,char * file,int mask)211 _ecore_file_monitor_inotify_events(Ecore_File_Monitor *em, char *file, int mask)
212 {
213    char buf[PATH_MAX];
214    int isdir;
215 
216    if ((file) && (file[0]))
217      snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
218    else
219      {
220         strncpy(buf, em->path, sizeof(buf));
221         buf[PATH_MAX - 1] = 0;
222      }
223    isdir = mask & IN_ISDIR;
224 
225 #if 0
226    _ecore_file_monitor_inotify_print(buf, mask);
227 #endif
228 
229    if (mask & IN_ATTRIB)
230      {
231         em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
232      }
233    if (mask & IN_CLOSE_WRITE)
234      {
235         if (!isdir)
236           em->func(em->data, em, ECORE_FILE_EVENT_CLOSED, buf);
237      }
238    if (mask & IN_MODIFY)
239      {
240         if (!isdir)
241           em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
242      }
243    if (mask & IN_MOVED_FROM)
244      {
245         if (isdir)
246           em->func(em->data, em, ECORE_FILE_EVENT_DELETED_DIRECTORY, buf);
247         else
248           em->func(em->data, em, ECORE_FILE_EVENT_DELETED_FILE, buf);
249      }
250    if (mask & IN_MOVED_TO)
251      {
252         if (isdir)
253           em->func(em->data, em, ECORE_FILE_EVENT_CREATED_DIRECTORY, buf);
254         else
255           em->func(em->data, em, ECORE_FILE_EVENT_CREATED_FILE, buf);
256      }
257    if (mask & IN_DELETE)
258      {
259         if (isdir)
260           em->func(em->data, em, ECORE_FILE_EVENT_DELETED_DIRECTORY, buf);
261         else
262           em->func(em->data, em, ECORE_FILE_EVENT_DELETED_FILE, buf);
263      }
264    if (mask & IN_CREATE)
265      {
266         if (isdir)
267           em->func(em->data, em, ECORE_FILE_EVENT_CREATED_DIRECTORY, buf);
268         else
269           em->func(em->data, em, ECORE_FILE_EVENT_CREATED_FILE, buf);
270      }
271    if (mask & IN_DELETE_SELF)
272      {
273         em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
274      }
275    if (mask & IN_MOVE_SELF)
276      {
277         /* We just call delete. The dir is gone... */
278         em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
279      }
280    if (mask & IN_UNMOUNT)
281      {
282         /* We just call delete. The dir is gone... */
283         em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
284      }
285    if (mask & IN_IGNORED)
286      /* The watch is removed. If the file name still exists monitor the new one,
287       * else delete it */
288      _ecore_file_monitor_inotify_monitor(em, em->path);
289 }
290 
291 static int
_ecore_file_monitor_inotify_monitor(Ecore_File_Monitor * em,const char * path)292 _ecore_file_monitor_inotify_monitor(Ecore_File_Monitor *em, const char *path)
293 {
294    int mask = IN_ATTRIB | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO |
295      IN_DELETE | IN_CREATE | IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF |
296      IN_UNMOUNT;
297 
298    ECORE_FILE_MONITOR_INOTIFY(em)->wd =
299       inotify_add_watch(ecore_main_fd_handler_fd_get(_fdh), path, mask);
300    if (ECORE_FILE_MONITOR_INOTIFY(em)->wd < 0)
301      {
302         INF("inotify_add_watch failed, %s", strerror(errno));
303         ecore_file_monitor_backend_del(em);
304         return 0;
305      }
306    eina_hash_list_append(monitor_hash, &ECORE_FILE_MONITOR_INOTIFY(em)->wd, em);
307    return 1;
308 }
309 
310 #if 0
311 static void
312 _ecore_file_monitor_inotify_print(char *file, int mask)
313 {
314    const char *type;
315 
316    if (mask & IN_ISDIR)
317      type = "dir";
318    else
319      type = "file";
320 
321    if (mask & IN_ACCESS)
322      INF("Inotify accessed %s: %s", type, file);
323    if (mask & IN_MODIFY)
324      INF("Inotify modified %s: %s", type, file);
325    if (mask & IN_ATTRIB)
326      INF("Inotify attributes %s: %s", type, file);
327    if (mask & IN_CLOSE_WRITE)
328      INF("Inotify close write %s: %s", type, file);
329    if (mask & IN_CLOSE_NOWRITE)
330      INF("Inotify close write %s: %s", type, file);
331    if (mask & IN_OPEN)
332      INF("Inotify open %s: %s", type, file);
333    if (mask & IN_MOVED_FROM)
334      INF("Inotify moved from %s: %s", type, file);
335    if (mask & IN_MOVED_TO)
336      INF("Inotify moved to %s: %s", type, file);
337    if (mask & IN_DELETE)
338      INF("Inotify delete %s: %s", type, file);
339    if (mask & IN_CREATE)
340      INF("Inotify create %s: %s", type, file);
341    if (mask & IN_DELETE_SELF)
342      INF("Inotify delete self %s: %s", type, file);
343    if (mask & IN_MOVE_SELF)
344      INF("Inotify move self %s: %s", type, file);
345    if (mask & IN_UNMOUNT)
346      INF("Inotify unmount %s: %s", type, file);
347 }
348 #endif
349