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