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