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