Tizen 2.0 Release
[apps/core/preloaded/myfiles.git] / src / common / mf-inotify-handle.c
1 /*
2  * Copyright 2013         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 #include <stdio.h>
21 #include <glib.h>
22 #include <sys/inotify.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <stdint.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <pthread.h>
30
31 #include "mf-dlog.h"
32 #include "mf-inotify-handle.h"
33
34 #define MF_WATCH_FLAGS \
35         IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE
36
37 #define MF_EVENT_SIZE  (sizeof(struct inotify_event))
38 /** reasonable guess as to size of 1024 events */
39 #define MF_EVENT_BUF_LEN (1024 * (MF_EVENT_SIZE + 16))
40 #define MF_U32_MAX      0xFFFFFFFF
41
42 int flagInotifyMsg = 1;
43 pthread_cond_t g_cond_inotify_msg;
44
45 typedef struct _mf_inotify_t {
46         int fd;
47         int wd;
48         gchar *path;
49         unsigned int prev_event;
50         pthread_t monitor;
51         mf_inotify_cb callback;
52         void *u_data;
53 } mf_inotify_t;
54
55 static pthread_mutex_t mf_noti_lock;
56 static mf_inotify_t *g_handle = NULL;
57
58 static void __mf_inotify_handle_free(void)
59 {
60         pthread_mutex_destroy(&mf_noti_lock);
61
62         if (g_handle) {
63                 if (g_handle->fd >= 0) {
64                         close(g_handle->fd);
65                         g_handle->fd = -1;
66                 }
67                 if (g_handle->path) {
68                         g_free(g_handle->path);
69                         g_handle->path = NULL;
70                 }
71                 g_free(g_handle);
72                 g_handle = NULL;
73         }
74
75         return;
76 }
77
78 static mf_inotify_t *__mf_inotify_handle_init(void)
79 {
80         __mf_inotify_handle_free();
81         g_handle = g_new0(mf_inotify_t, 1);
82
83         if (g_handle) {
84                 g_handle->fd = -1;
85                 g_handle->wd = -1;
86                 pthread_mutex_init(&mf_noti_lock, NULL);
87         }
88
89         return g_handle;
90 }
91
92 static void __mf_inotify_handle_thread_clean_up(void *data)
93 {
94         pthread_mutex_t *lock = (pthread_mutex_t *) data;
95         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_inotify_handle_watch_thread(gpointer user_data)
104 {
105         mf_inotify_t *handle = (mf_inotify_t *) user_data;
106         int oldtype = 0;
107
108         mf_retvm_if(handle == NULL, NULL, "handle is NULL");
109         mf_debug("Create __mf_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                         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                         mf_error("Fail to read() -fd : %d,  len : %d", handle->fd, len);
126                         continue;
127                 }
128
129                 flagInotifyMsg = 1;
130
131                 while (i < len) {
132                         struct inotify_event *pevent = (struct inotify_event *)&event_buff[i];
133                         mf_inotify_event s_event = MF_INOTI_NONE;
134                         mf_debug("mask=%x dir=%s len=%d name=%s",
135                                  pevent->mask, (pevent->mask & IN_ISDIR) ? "yes" : "no", pevent->len, (pevent->len) ? pevent->name : NULL);
136
137                         if (pevent->len && strncmp(pevent->name, ".", 1) == 0) {
138                                 s_event = MF_INOTI_NONE;
139                         } else if (pevent->mask & IN_ISDIR) {
140                                 if (pevent->mask & IN_DELETE_SELF)
141                                         s_event = MF_INOTI_DELETE_SELF;
142
143                                 if (pevent->mask & IN_MOVE_SELF)
144                                         s_event = MF_INOTI_MOVE_SELF;
145
146                                 if (pevent->mask & IN_CREATE)
147                                         s_event = MF_INOTI_CREATE;
148
149                                 if (pevent->mask & IN_DELETE)
150                                         s_event = MF_INOTI_DELETE;
151
152                                 if (pevent->mask & IN_MOVED_FROM)
153                                         s_event = MF_INOTI_MOVE_OUT;
154
155                                 if (pevent->mask & IN_MOVED_TO)
156                                         s_event = MF_INOTI_MOVE_IN;
157                         } else {
158                                 if (pevent->mask & IN_CREATE) {
159                                         s_event = MF_INOTI_NONE;
160                                         handle->prev_event = IN_CREATE;
161                                 }
162
163                                 if (pevent->mask & IN_CLOSE_WRITE) {
164                                         if (handle->prev_event == IN_CREATE) {
165                                                 s_event = MF_INOTI_CREATE;
166                                         } else {
167                                                 s_event = MF_INOTI_MODIFY;
168                                         }
169                                         handle->prev_event = 0;
170                                 }
171
172                                 if (pevent->mask & IN_DELETE)
173                                         s_event = MF_INOTI_DELETE;
174
175                                 if (pevent->mask & IN_MOVED_FROM)
176                                         s_event = MF_INOTI_MOVE_OUT;
177
178                                 if (pevent->mask & IN_MOVED_TO)
179                                         s_event = MF_INOTI_MOVE_IN;
180                         }
181
182                         mf_debug("s_event : %d, prev_event: %x, callback : %p", s_event, handle->prev_event, handle->callback);
183                         if (s_event != MF_INOTI_NONE) {
184                                 pthread_cleanup_push(__mf_inotify_handle_thread_clean_up, (void *)&mf_noti_lock);
185                                 pthread_mutex_lock(&mf_noti_lock);
186
187                                 while (flagInotifyMsg == 0) {
188                                         mf_debug("!!!!!!!!!!!! wait");
189                                         pthread_cond_wait(&g_cond_inotify_msg, &mf_noti_lock);
190                                 }
191                                 flagInotifyMsg = 0;
192
193                                 if (handle->callback) {
194                                         handle->callback(s_event, (pevent->len) ? pevent->name : NULL, handle->u_data);
195                                 }
196                                 pthread_mutex_unlock(&mf_noti_lock);
197                                 pthread_cleanup_pop(0);
198                         }
199
200                         if ((MF_U32_MAX - pevent->len) >=  MF_EVENT_SIZE) {
201                                 i += sizeof(struct inotify_event) + pevent->len;
202                         } else {
203                                 break;
204                         }
205                 }
206         }
207
208         mf_debug("end __mf_inotify_handle_watch_thread!!! ");
209
210         return NULL;
211 }
212
213 void mf_inotify_handle_request_handled_send()
214 {
215         pthread_mutex_lock(&mf_noti_lock);
216         if (flagInotifyMsg == 0) {
217                 flagInotifyMsg = 1;
218                 pthread_cond_signal(&g_cond_inotify_msg);
219         }
220         pthread_mutex_unlock(&mf_noti_lock);
221 }
222
223 int mf_inotify_handle_init_inotify(void)
224 {
225         mf_inotify_t *handle = NULL;
226         handle = __mf_inotify_handle_init();
227         mf_retvm_if(handle == NULL, -1, "fail to __mf_inotify_handle_init()");
228
229         handle->fd = inotify_init();
230
231         if (handle->fd < 0) {
232                 switch (errno) {
233                 case EMFILE:
234                         mf_error("The user limit on the total number of inotify instances has been reached.\n");
235                         break;
236                 case ENFILE:
237                         mf_error("The system limit on the total number of file descriptors has been reached.\n");
238                         break;
239                 case ENOMEM:
240                         mf_error("Insufficient kernel memory is available.\n");
241                         break;
242                 default:
243                         mf_error("Fail to inotify_init(), Unknown error.\n");
244                         break;
245                 }
246                 return -1;
247         }
248         pthread_create(&handle->monitor, NULL, __mf_inotify_handle_watch_thread, handle);
249         return 0;
250 }
251
252 int mf_inotify_handle_add_watch(const char *path, mf_inotify_cb callback, void *user_data)
253 {
254         mf_inotify_t *handle = NULL;
255         handle = g_handle;
256         mf_retvm_if(handle == NULL, -1, "handle is NULL");
257
258         if (handle->wd >= 0) {
259                 mf_warnig("The mf_notify module supports single instance, the watch descript [%d] is removed automatically\n", handle->wd);
260                 mf_inotify_handle_rm_watch();
261         }
262
263         pthread_mutex_lock(&mf_noti_lock);
264         handle->wd = inotify_add_watch(handle->fd, path, MF_WATCH_FLAGS);
265
266         if (handle->wd < 0) {
267                 switch (errno) {
268                 case EACCES:
269                         mf_error("Read access to the given file is not permitted.\n");
270                         break;
271                 case EBADF:
272                         mf_error("The given file descriptor is not valid.\n");
273                         handle->fd = -1;
274                         break;
275                 case EFAULT:
276                         mf_error("pathname points outside of the process's accessible address space.\n");
277                         break;
278                 case EINVAL:
279                         mf_error("The given event mask contains no legal events; or fd is not an inotify file descriptor.\n");
280                         break;
281                 case ENOMEM:
282                         mf_error("Insufficient kernel memory is available.\n");
283                         break;
284                 case ENOSPC:
285                         mf_error("User limit on the total number of inotify watches was reached or the kernel failed to alloc a needed resource.\n");
286                         break;
287                 default:
288                         mf_error("Fail to mf_inotify_handle_add_watch(), Unknown error.\n");
289                         break;
290                 }
291                 pthread_mutex_unlock(&mf_noti_lock);
292                 return -1;
293         }
294
295         mf_debug("start watching [%s] directory", path);
296         if (handle->path) {
297                 g_free(handle->path);
298                 handle->path = NULL;
299         }
300         handle->path = g_strdup(path);
301         handle->callback = callback;
302         handle->u_data = user_data;
303         pthread_mutex_unlock(&mf_noti_lock);
304
305         return 0;
306 }
307
308
309
310 int mf_inotify_handle_rm_watch(void)
311 {
312         int ret = -1;
313         mf_inotify_t *handle = NULL;
314
315         handle = g_handle;
316         mf_retvm_if(handle == NULL, -1, "handle is NULL");
317
318         if (handle->fd < 0 || handle->wd < 0) {
319                 mf_warnig("inotify is not initialized or has no watching dir - fd [%d] wd [%d]", handle->fd, handle->wd);
320                 return 0;
321         }
322
323         pthread_mutex_lock(&mf_noti_lock);
324
325         ret = inotify_rm_watch(handle->fd, handle->wd);
326         if (ret < 0) {
327                 switch (errno) {
328                 case EBADF:
329                         mf_error("fd is not a valid file descriptor\n");
330                         handle->fd = -1;
331                         break;
332                 case EINVAL:
333                         mf_error("The watch descriptor wd is not valid; or fd is not an inotify file descriptor.\n");
334                         handle->wd = -1;
335                         break;
336                 default:
337                         mf_error("Fail to mf_inotify_handle_add_watch(), Unknown error.\n");
338                         break;
339                 }
340                 pthread_mutex_unlock(&mf_noti_lock);
341                 return -1;
342         }
343         mf_debug("stop watching [%s] directory", handle->path);
344         if (handle->path) {
345                 g_free(handle->path);
346                 handle->path = NULL;
347         }
348         handle->callback = NULL;
349         handle->u_data = NULL;
350         handle->wd = -1;
351         pthread_mutex_unlock(&mf_noti_lock);
352
353         return 0;
354 }
355
356 void mf_inotify_handle_finalize_inotify(void)
357 {
358         mf_inotify_t *handle = NULL;
359         handle = g_handle;
360
361         mf_retm_if(handle == NULL, "handle is NULL");
362
363         if (handle->fd >= 0 && handle->wd >= 0) {
364                 mf_inotify_handle_rm_watch();
365         }
366
367         pthread_cancel(handle->monitor);
368         pthread_join(handle->monitor, NULL);
369
370         __mf_inotify_handle_free();
371
372         return;
373 }