modify terminology:calendar->book,allday->localtime,normal->utime,svc->service
[platform/core/pim/calendar-service.git] / common / cal_inotify.c
1 /*
2  * Calendar Service
3  *
4  * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  */
19
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sys/types.h>
25 #include <sys/inotify.h>
26
27 #include "cal_internal.h"
28 #include "cal_typedef.h"
29 #include "cal_view.h"
30 #include "cal_inotify.h"
31 #include "cal_utils.h"
32
33 #ifdef CAL_IPC_CLIENT
34 #include "cal_mutex.h"
35 #endif
36
37 typedef struct {
38         int wd;
39         calendar_db_changed_cb cb;
40         void *cb_data;
41         cal_noti_type_e noti_type;
42         bool blocked;
43 } noti_info_s;
44
45 static int _inoti_fd = -1;
46 static guint inoti_handler;
47 static GSList *_noti_list;
48
49 #ifdef CAL_IPC_CLIENT
50 static int calendar_inoti_count = 0;
51 #endif
52
53 static inline void _handle_callback(GSList *_noti_list, int wd, uint32_t mask)
54 {
55         GSList *cursor;
56
57         cursor = _noti_list;
58         while (cursor) {
59                 noti_info_s *noti = NULL;
60                 noti = (noti_info_s *)cursor->data;
61                 if (noti->wd == wd) {
62                         if ((mask & IN_CLOSE_WRITE) && noti->cb) {
63                                 switch (noti->noti_type) {
64                                 case CAL_NOTI_TYPE_BOOK:
65                                         noti->cb(CALENDAR_VIEW_BOOK, noti->cb_data);
66                                         break;
67                                 case CAL_NOTI_TYPE_EVENT:
68                                         noti->cb(CALENDAR_VIEW_EVENT, noti->cb_data);
69                                         break;
70                                 case CAL_NOTI_TYPE_TODO:
71                                         noti->cb(CALENDAR_VIEW_TODO, noti->cb_data);
72                                         break;
73                                 default:
74                                         break;
75                                 }
76                         }
77                 }
78                 cursor = cursor->next;
79         }
80 }
81
82 static gboolean _inotify_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
83 {
84         int fd, ret;
85         struct inotify_event ie;
86         char name[FILENAME_MAX] = {0};
87
88         fd = g_io_channel_unix_get_fd(src);
89
90         while (0 < (ret = read(fd, &ie, sizeof(ie)))) {
91                 if (sizeof(ie) == ret) {
92                         if (_noti_list)
93                                 _handle_callback(_noti_list, ie.wd, ie.mask);
94
95                         while (0 != ie.len) {
96                                 ret = read(fd, name, (ie.len < sizeof(name)) ? ie.len : sizeof(name));
97                                 if (-1 == ret) {
98                                         if (EINTR == errno)
99                                                 continue;
100                                         else
101                                                 return TRUE;
102                                 }
103                                 if (ie.len < ret)
104                                         ie.len = 0;
105                                 else
106                                         ie.len -= ret;
107                         }
108                 } else {
109                         while (ret < sizeof(ie)) {
110                                 int read_size;
111                                 read_size = read(fd, name, sizeof(ie)-ret);
112                                 if (-1 == read_size) {
113                                         if (EINTR == errno)
114                                                 continue;
115                                         else
116                                                 return TRUE;
117                                 }
118                                 ret += read_size;
119                         }
120                 }
121         }
122         return TRUE;
123 }
124
125 static inline int _inotify_attach_handler(int fd)
126 {
127         guint ret;
128         GIOChannel *channel;
129
130         if (fd < 0) {
131                 /* LCOV_EXCL_START */
132                 ERR("Invalid argument: fd is NULL");
133                 return CALENDAR_ERROR_INVALID_PARAMETER;
134                 /* LCOV_EXCL_STOP */
135         }
136
137         channel = g_io_channel_unix_new(fd);
138         if (NULL == channel) {
139                 /* LCOV_EXCL_START */
140                 ERR("g_io_channel_unix_new() Fail");
141                 return -1; /* CALENDAR_ERROR_FAILED_INOTIFY */
142                 /* LCOV_EXCL_STOP */
143         }
144
145         g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
146
147         ret = g_io_add_watch(channel, G_IO_IN, _inotify_gio_cb, NULL);
148         g_io_channel_unref(channel);
149
150         return ret;
151 }
152
153 int cal_inotify_init(void)
154 {
155         int ret;
156
157 #ifdef CAL_IPC_CLIENT
158         cal_mutex_lock(CAL_MUTEX_INOTIFY);
159         calendar_inoti_count++;
160
161         if (1 < calendar_inoti_count) {
162                 DBG("inotify count =%d", calendar_inoti_count);
163                 cal_mutex_unlock(CAL_MUTEX_INOTIFY);
164                 return CALENDAR_ERROR_NONE;
165         }
166         DBG("inotify count =%d", calendar_inoti_count);
167         cal_mutex_unlock(CAL_MUTEX_INOTIFY);
168 #endif
169         _inoti_fd = inotify_init();
170         if (_inoti_fd == -1) {
171                 /* LCOV_EXCL_START */
172                 ERR("inotify_init() Fail(%d)", errno);
173 #ifdef CAL_IPC_CLIENT
174                 cal_mutex_lock(CAL_MUTEX_INOTIFY);
175                 calendar_inoti_count = 0;
176                 cal_mutex_unlock(CAL_MUTEX_INOTIFY);
177 #endif
178                 return -1; /* CALENDAR_ERROR_FAILED_INOTIFY */
179                 /* LCOV_EXCL_STOP */
180         }
181
182         ret = fcntl(_inoti_fd, F_SETFD, FD_CLOEXEC);
183         WARN_IF(ret < 0, "fcntl failed(%d)", ret);
184         ret = fcntl(_inoti_fd, F_SETFL, O_NONBLOCK);
185         WARN_IF(ret < 0, "fcntl failed(%d)", ret);
186
187         inoti_handler = _inotify_attach_handler(_inoti_fd);
188         if (inoti_handler <= 0) {
189                 /* LCOV_EXCL_START */
190                 ERR("_inotify_attach_handler() Fail");
191                 close(_inoti_fd);
192                 _inoti_fd = -1;
193                 inoti_handler = 0;
194 #ifdef CAL_IPC_CLIENT
195                 cal_mutex_lock(CAL_MUTEX_INOTIFY);
196                 calendar_inoti_count = 0;
197                 cal_mutex_unlock(CAL_MUTEX_INOTIFY);
198 #endif
199                 return -1; /* CALENDAR_ERROR_FAILED_INOTIFY */
200                 /* LCOV_EXCL_STOP */
201         }
202
203         return CALENDAR_ERROR_NONE;
204 }
205
206 static inline int _cal_inotify_get_wd(int fd, const char *notipath)
207 {
208         return inotify_add_watch(fd, notipath, IN_ACCESS);
209 }
210
211 static inline int _cal_inotify_add_watch(int fd, const char *notipath)
212 {
213         int ret;
214
215         ret = inotify_add_watch(fd, notipath, IN_CLOSE_WRITE);
216         if (ret < 0) {
217                 /* LCOV_EXCL_START */
218                 ERR("Failed to add watch(ret:%d)", ret);
219                 return -1; /* CALENDAR_ERROR_FAILED_INOTIFY */
220                 /* LCOV_EXCL_STOP */
221         }
222
223         return CALENDAR_ERROR_NONE;
224 }
225
226 static bool _has_noti(int wd, void *cb, void *cb_data)
227 {
228         bool has_noti = false;
229         GSList *cursor = NULL;
230         cursor = _noti_list;
231         while (cursor) {
232                 noti_info_s *info = (noti_info_s *)cursor->data;
233                 if (NULL == info) {
234                         WARN("No info");
235                         cursor = g_slist_next(cursor);
236                         continue;
237                 }
238
239                 if (info->wd == wd && info->cb == cb && info->cb_data == cb_data)
240                         has_noti = true;
241
242                 cursor = g_slist_next(cursor);
243         }
244         return has_noti;
245 }
246
247 static int _append_noti(int wd, int type, void *cb, void *cb_data)
248 {
249         noti_info_s *info = NULL;
250         info = calloc(1, sizeof(noti_info_s));
251         if (NULL == info) {
252                 /* LCOV_EXCL_START */
253                 ERR("calloc() Fail");
254                 return CALENDAR_ERROR_OUT_OF_MEMORY;
255                 /* LCOV_EXCL_STOP */
256         }
257
258         info->wd = wd;
259         info->noti_type = type;
260         info->cb_data = cb_data;
261         info->cb = cb;
262         info->blocked = false;
263
264         _noti_list = g_slist_append(_noti_list, info);
265
266         return CALENDAR_ERROR_NONE;
267 }
268
269 int cal_inotify_subscribe(cal_noti_type_e type, const char *path, void *cb, void *cb_data)
270 {
271         int ret, wd;
272
273         RETV_IF(NULL == path, CALENDAR_ERROR_INVALID_PARAMETER);
274         RETV_IF(NULL == cb, CALENDAR_ERROR_INVALID_PARAMETER);
275         RETVM_IF(_inoti_fd < 0, CALENDAR_ERROR_INVALID_PARAMETER, "_inoti_fd(%d) is invalid", _inoti_fd);
276
277         wd = _cal_inotify_get_wd(_inoti_fd, path);
278         if (wd == -1) {
279                 /* LCOV_EXCL_START */
280                 ERR("_cal_inotify_get_wd() Fail(%d)", errno);
281                 if (errno == EACCES)
282                         return CALENDAR_ERROR_PERMISSION_DENIED;
283                 return CALENDAR_ERROR_SYSTEM;
284                 /* LCOV_EXCL_STOP */
285         }
286
287         if (true == _has_noti(wd, cb, cb_data)) {
288                 /* LCOV_EXCL_START */
289                 ERR("noti is already registered: path[%s]", path);
290                 _cal_inotify_add_watch(_inoti_fd, path);
291                 return CALENDAR_ERROR_INVALID_PARAMETER;
292                 /* LCOV_EXCL_STOP */
293         }
294
295         ret = _cal_inotify_add_watch(_inoti_fd, path);
296         if (CALENDAR_ERROR_NONE != ret) {
297                 /* LCOV_EXCL_START */
298                 ERR("_cal_inotify_add_watch() Fail(%d)", ret);
299                 return ret;
300                 /* LCOV_EXCL_STOP */
301         }
302         _append_noti(wd, type, cb, cb_data);
303
304         return CALENDAR_ERROR_NONE;
305 }
306
307 static int _cal_del_noti(GSList **_noti_list, int wd, void *cb, void *cb_data)
308 {
309         int del_cnt, remain_cnt;
310         GSList *cursor, *result;
311
312         del_cnt = 0;
313         remain_cnt = 0;
314
315         cursor = result = *_noti_list;
316         while (cursor) {
317                 noti_info_s *noti = cursor->data;
318                 if (noti && wd == noti->wd) {
319                         if (cb == noti->cb && cb_data == noti->cb_data) {
320                                 cursor = g_slist_next(cursor);
321                                 result = g_slist_remove(result , noti);
322                                 free(noti);
323                                 del_cnt++;
324                                 continue;
325                         } else {
326                                 remain_cnt++;
327                         }
328                 }
329                 cursor = g_slist_next(cursor);
330         }
331
332         if (del_cnt == 0) {
333                 /* LCOV_EXCL_START */
334                 ERR("Nothing to delete");
335                 return CALENDAR_ERROR_NO_DATA;
336                 /* LCOV_EXCL_STOP */
337         }
338         *_noti_list = result;
339
340         return remain_cnt;
341 }
342
343 int cal_inotify_unsubscribe(const char *path, void *cb, void *cb_data)
344 {
345         int ret, wd;
346
347         RETV_IF(NULL == path, CALENDAR_ERROR_INVALID_PARAMETER);
348         RETV_IF(NULL == cb, CALENDAR_ERROR_INVALID_PARAMETER);
349         RETVM_IF(_inoti_fd < 0, CALENDAR_ERROR_SYSTEM, "System : _inoti_fd(%d) is invalid", _inoti_fd);
350
351         wd = _cal_inotify_get_wd(_inoti_fd, path);
352         if (wd == -1) {
353                 /* LCOV_EXCL_START */
354                 ERR("_cal_inotify_get_wd() Fail(%d)", errno);
355                 if (errno == EACCES)
356                         return CALENDAR_ERROR_PERMISSION_DENIED;
357                 return CALENDAR_ERROR_SYSTEM;
358                 /* LCOV_EXCL_STOP */
359         }
360
361         ret = _cal_del_noti(&_noti_list, wd, cb, cb_data);
362         WARN_IF(ret < CALENDAR_ERROR_NONE, "_cal_del_noti() Fail(%d)", ret);
363
364         if (CALENDAR_ERROR_NONE != ret) {
365                 ret = _cal_inotify_add_watch(_inoti_fd, path);
366                 return ret;
367         }
368
369         return inotify_rm_watch(_inoti_fd, wd);
370 }
371
372 static inline gboolean _cal_inotify_detach_handler(guint id)
373 {
374         return g_source_remove(id);
375 }
376
377 static void __clear_nslot_list(gpointer data, gpointer user_data)
378 {
379         noti_info_s *noti = (noti_info_s *)data;
380         free(noti);
381 }
382
383 void cal_inotify_deinit(void)
384 {
385 #ifdef CAL_IPC_CLIENT
386         cal_mutex_lock(CAL_MUTEX_INOTIFY);
387         calendar_inoti_count--;
388
389         if (0 < calendar_inoti_count) {
390                 DBG("inotify count =%d", calendar_inoti_count);
391                 cal_mutex_unlock(CAL_MUTEX_INOTIFY);
392                 return ;
393         }
394         DBG("inotify count =%d", calendar_inoti_count);
395         cal_mutex_unlock(CAL_MUTEX_INOTIFY);
396 #endif
397         if (inoti_handler) {
398                 _cal_inotify_detach_handler(inoti_handler);
399                 inoti_handler = 0;
400         }
401
402         if (_noti_list) {
403                 g_slist_foreach(_noti_list, __clear_nslot_list, NULL);
404                 g_slist_free(_noti_list);
405                 _noti_list = NULL;
406         }
407
408         if (0 <= _inoti_fd) {
409                 close(_inoti_fd);
410                 _inoti_fd = -1;
411         }
412 }