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