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