Tizen 2.1 base
[apps/home/ug-myfile-efl.git] / src / common / mf-ug-inotify-handle.c
1 /*
2  * Copyright 2012          Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *  http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18
19
20
21
22 #include <stdio.h>
23 #include <glib.h>
24 #include <sys/inotify.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <stdint.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <pthread.h>
32
33 #include "mf-ug-dlog.h"
34 #include "mf-ug-inotify-handle.h"
35
36 #define MF_WATCH_FLAGS \
37         IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE
38
39 #define MF_EVENT_SIZE  (sizeof(struct inotify_event))
40 /** reasonable guess as to size of 1024 events */
41 #define MF_EVENT_BUF_LEN (1024 * (MF_EVENT_SIZE + 16))
42 #define MF_U32_MAX              0xFFFFFFFF
43 typedef struct _mf_inotify_t {
44         int fd;
45         int wd;
46         gchar *path;
47         unsigned int prev_event;
48         pthread_t monitor;
49         mf_ug_inotify_cb callback;
50         void *u_data;
51 } mf_inotify_t;
52
53 static pthread_mutex_t mf_noti_lock;
54 static mf_inotify_t *g_handle;
55
56 static void __mf_ug_inotify_handle_free_handle(void)
57 {
58         pthread_mutex_destroy(&mf_noti_lock);
59
60         if (g_handle) {
61                 if (g_handle->fd >= 0) {
62                         close(g_handle->fd);
63                         g_handle->fd = -1;
64                 }
65                 if (g_handle->path) {
66                         g_free(g_handle->path);
67                         g_handle->path = NULL;
68                 }
69                 g_free(g_handle);
70                 g_handle = NULL;
71         }
72
73         return;
74 }
75
76 static mf_inotify_t *__mf_ug_inotify_handle_init_handle(void)
77 {
78         __mf_ug_inotify_handle_free_handle();
79         g_handle = g_new0(mf_inotify_t, 1);
80
81         if (g_handle) {
82                 g_handle->fd = -1;
83                 pthread_mutex_init(&mf_noti_lock, NULL);
84                 pthread_mutex_lock(&mf_noti_lock);
85                 g_handle->wd = -1;
86                 pthread_mutex_unlock(&mf_noti_lock);
87         }
88
89         return g_handle;
90 }
91
92 static void __mf_ug_inotify_handle_clean_up_thread(void *data)
93 {
94         pthread_mutex_t *lock = (pthread_mutex_t *) data;
95         ug_mf_debug("Thread cancel Clean_up function");
96         if (lock) {
97                 pthread_mutex_unlock(lock);
98         }
99         return;
100 }
101
102
103 static gpointer __mf_ug_inotify_handle_watch_thread(gpointer user_data)
104 {
105         mf_inotify_t *handle = (mf_inotify_t *) user_data;
106         int oldtype = 0;
107
108         ug_mf_retvm_if(handle == NULL, NULL, "handle is NULL");
109         ug_mf_debug("Create __mf_ug_inotify_handle_watch_thread!!! ");
110
111         pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
112
113         while (1) {
114                 ssize_t len = 0;
115                 uint32_t i = 0;
116                 char event_buff[MF_EVENT_BUF_LEN] = { 0, };
117
118                 if (handle->fd < 0) {
119                         ug_mf_error("fd is not a vaild one");
120                         pthread_exit(NULL);
121                 }
122
123                 len = read(handle->fd, event_buff, sizeof(event_buff) - 1);
124                 if (len <= 0 || len > sizeof(event_buff) - 1) {
125                         ug_mf_error("Fail to read() -fd : %d,  len : %d", handle->fd, len);
126                         continue;
127                 }
128
129                 while (i < len) {
130                         struct inotify_event *pevent = (struct inotify_event *)&event_buff[i];
131                         mf_ug_inotify_event s_event = UG_MF_INOTI_NONE;
132                         ug_mf_error("mask=%x dir=%s len=%d name=%s",
133                                     pevent->mask, (pevent->mask & IN_ISDIR) ? "yes" : "no", pevent->len, (pevent->len) ? pevent->name : NULL);
134
135                         if (pevent->len && strncmp(pevent->name, ".", 1) == 0) {
136                                 s_event = UG_MF_INOTI_NONE;
137                         } else if (pevent->mask & IN_ISDIR) {
138                                 if (pevent->mask & IN_DELETE_SELF)
139                                         s_event = UG_MF_INOTI_DELETE_SELF;
140
141                                 if (pevent->mask & IN_MOVE_SELF)
142                                         s_event = UG_MF_INOTI_MOVE_SELF;
143
144                                 if (pevent->mask & IN_CREATE)
145                                         s_event = UG_MF_INOTI_CREATE;
146
147                                 if (pevent->mask & IN_DELETE)
148                                         s_event = UG_MF_INOTI_DELETE;
149
150                                 if (pevent->mask & IN_MOVED_FROM)
151                                         s_event = UG_MF_INOTI_MOVE_OUT;
152
153                                 if (pevent->mask & IN_MOVED_TO)
154                                         s_event = UG_MF_INOTI_MOVE_IN;
155                         } else {
156                                 if (pevent->mask & IN_CREATE) {
157                                         s_event = UG_MF_INOTI_NONE;
158                                         handle->prev_event = IN_CREATE;
159                                 }
160
161                                 if (pevent->mask & IN_CLOSE_WRITE) {
162                                         if (handle->prev_event == IN_CREATE) {
163                                                 s_event = UG_MF_INOTI_CREATE;
164                                         } else {
165                                                 s_event = UG_MF_INOTI_MODIFY;
166                                         }
167                                         handle->prev_event = 0;
168                                 }
169
170                                 if (pevent->mask & IN_DELETE)
171                                         s_event = UG_MF_INOTI_DELETE;
172
173                                 if (pevent->mask & IN_MOVED_FROM)
174                                         s_event = UG_MF_INOTI_MOVE_OUT;
175
176                                 if (pevent->mask & IN_MOVED_TO)
177                                         s_event = UG_MF_INOTI_MOVE_IN;
178                         }
179
180                         ug_mf_debug("s_event : %d, prev_event: %x, callback : %p", s_event, handle->prev_event, handle->callback);
181                         if (s_event != UG_MF_INOTI_NONE) {
182                                 pthread_cleanup_push(__mf_ug_inotify_handle_clean_up_thread, (void *)&mf_noti_lock);
183                                 pthread_mutex_lock(&mf_noti_lock);
184                                 if (handle->callback) {
185                                         handle->callback(s_event, (pevent->len) ? pevent->name : NULL, handle->u_data);
186                                 }
187                                 pthread_mutex_unlock(&mf_noti_lock);
188                                 pthread_cleanup_pop(0);
189                         }
190
191                         if ((MF_U32_MAX - pevent->len) >=  MF_EVENT_SIZE) {
192                                 i += sizeof(struct inotify_event) + pevent->len;
193                         } else {
194                                 break;
195                         }
196                 }
197         }
198
199         ug_mf_debug("end __mf_ug_inotify_handle_watch_thread!!! ");
200
201         return NULL;
202 }
203
204 int mf_ug_inotify_handle_init_inotify(void)
205 {
206         mf_inotify_t *handle = NULL;
207         handle = __mf_ug_inotify_handle_init_handle();
208         ug_mf_retvm_if(handle == NULL, -1, "fail to __mf_ug_inotify_handle_init_handle()");
209
210         handle->fd = inotify_init();
211
212         if (handle->fd < 0) {
213                 switch (errno) {
214                 case EMFILE:
215                         ug_mf_error("The user limit on the total number of inotify instances has been reached.\n");
216                         break;
217                 case ENFILE:
218                         ug_mf_error("The system limit on the total number of file descriptors has been reached.\n");
219                         break;
220                 case ENOMEM:
221                         ug_mf_error("Insufficient kernel memory is available.\n");
222                         break;
223                 default:
224                         ug_mf_error("Fail to inotify_init(), Unknown error.\n");
225                         break;
226                 }
227                 return -1;
228         }
229         pthread_create(&handle->monitor, NULL, __mf_ug_inotify_handle_watch_thread, handle);
230         return 0;
231 }
232
233 int mf_ug_inotify_handle_add_inotify_watch(const char *path, mf_ug_inotify_cb callback, void *user_data)
234 {
235         mf_inotify_t *handle = NULL;
236         handle = g_handle;
237         ug_mf_retvm_if(handle == NULL, -1, "handle is NULL");
238
239         if (handle->wd >= 0) {
240                 ug_mf_warnig("The mf_notify module supports single instance, the watch descript [%d] is removed automatically\n", handle->wd);
241                 mf_ug_inotify_handle_rm_inotify_watch();
242         }
243
244         pthread_mutex_lock(&mf_noti_lock);
245         handle->wd = inotify_add_watch(handle->fd, path, MF_WATCH_FLAGS);
246
247         if (handle->wd < 0) {
248                 switch (errno) {
249                 case EACCES:
250                         ug_mf_error("Read access to the given file is not permitted.\n");
251                         break;
252                 case EBADF:
253                         ug_mf_error("The given file descriptor is not valid.\n");
254                         handle->fd = -1;
255                         break;
256                 case EFAULT:
257                         ug_mf_error("pathname points outside of the process's accessible address space.\n");
258                         break;
259                 case EINVAL:
260                         ug_mf_error("The given event mask contains no legal events; or fd is not an inotify file descriptor.\n");
261                         break;
262                 case ENOMEM:
263                         ug_mf_error("Insufficient kernel memory is available.\n");
264                         break;
265                 case ENOSPC:
266                         ug_mf_error("User limit on the total num of inotify watch was reached or the kernel failed to alloc a needed resource.\n");
267                         break;
268                 default:
269                         ug_mf_error("Fail to ug_ug_mf_inotify_add_watch(), Unknown error.\n");
270                         break;
271                 }
272                 pthread_mutex_unlock(&mf_noti_lock);
273                 return -1;
274         }
275
276         ug_mf_debug("start watching [%s] directory", path);
277         if (handle->path) {
278                 g_free(handle->path);
279                 handle->path = NULL;
280         }
281         handle->path = g_strdup(path);
282         handle->callback = callback;
283         handle->u_data = user_data;
284         pthread_mutex_unlock(&mf_noti_lock);
285
286         return 0;
287 }
288
289
290
291 int mf_ug_inotify_handle_rm_inotify_watch(void)
292 {
293         int ret = -1;
294         mf_inotify_t *handle = NULL;
295
296         handle = g_handle;
297         ug_mf_retvm_if(handle == NULL, -1, "handle is NULL");
298
299         if (handle->fd < 0 || handle->wd < 0) {
300                 ug_mf_warnig("inotify is not initialized or has no watching dir - fd [%d] wd [%d]", handle->fd, handle->wd);
301                 return 0;
302         }
303
304         pthread_mutex_lock(&mf_noti_lock);
305
306         ret = inotify_rm_watch(handle->fd, handle->wd);
307         if (ret < 0) {
308                 switch (errno) {
309                 case EBADF:
310                         ug_mf_error("fd is not a valid file descriptor\n");
311                         handle->fd = -1;
312                         break;
313                 case EINVAL:
314                         ug_mf_error("The watch descriptor wd is not valid; or fd is not an inotify file descriptor.\n");
315                         handle->wd = -1;
316                         break;
317                 default:
318                         ug_mf_error("Fail to mf_ug_inotify_handle_add_inotify_watch(), Unknown error.\n");
319                         break;
320                 }
321                 pthread_mutex_unlock(&mf_noti_lock);
322                 return -1;
323         }
324         ug_mf_debug("stop watching [%s] directory", handle->path);
325         if (handle->path) {
326                 g_free(handle->path);
327                 handle->path = NULL;
328         }
329         handle->callback = NULL;
330         handle->u_data = NULL;
331         handle->wd = -1;
332         pthread_mutex_unlock(&mf_noti_lock);
333
334         return 0;
335 }
336
337 void mf_ug_inotify_handle_finalize_inotify(void)
338 {
339         mf_inotify_t *handle = NULL;
340         handle = g_handle;
341
342         ug_mf_retm_if(handle == NULL, "handle is NULL");
343
344         if (handle->fd >= 0 && handle->wd >= 0) {
345                 mf_ug_inotify_handle_rm_inotify_watch();
346         }
347
348         pthread_cancel(handle->monitor);
349         pthread_join(handle->monitor, NULL);
350
351         __mf_ug_inotify_handle_free_handle();
352
353         return;
354 }