4 * Copyright (c) 2010 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include <sys/types.h>
23 #include <sys/inotify.h>
26 #include "ctsvc_internal.h"
27 #include "ctsvc_notify.h"
28 #include "ctsvc_view.h"
29 #include "ctsvc_handle.h"
30 #include "ctsvc_inotify.h"
34 #ifdef _CONTACTS_IPC_CLIENT
35 #include "ctsvc_client_ipc.h"
36 #include "ctsvc_client_utils.h"
42 contacts_db_changed_cb cb;
47 struct socket_init_noti_info {
54 static GHashTable *_ctsvc_socket_init_noti_table = NULL;
55 static int __ctsvc_inoti_ref = 0;
56 static int __inoti_fd = -1;
57 static guint __inoti_handler = 0;
58 static GSList *__noti_list = NULL;
60 static inline void __ctsvc_inotify_handle_callback(GSList *noti_list, int wd, uint32_t mask)
65 for (it = noti_list; it; it = it->next) {
66 noti = (noti_info *)it->data;
70 #ifdef _CONTACTS_IPC_CLIENT
71 if (ctsvc_ipc_is_busy()) {
77 if ((mask & IN_CLOSE_WRITE) && noti->cb) {
78 DBG("%s", noti->view_uri);
79 noti->cb(noti->view_uri, noti->cb_data);
85 static void _ctsvc_inotify_socket_init_noti_table_foreach_cb(gpointer key, gpointer value, gpointer user_data)
87 struct socket_init_noti_info *noti_info = value;
89 int wd = GPOINTER_TO_INT(user_data);
90 if (noti_info->wd == wd)
91 noti_info->cb(noti_info->cb_data);
94 static gboolean __ctsvc_inotify_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
97 struct inotify_event ie;
98 char name[FILENAME_MAX] = {0};
100 fd = g_io_channel_unix_get_fd(src);
102 while (0 < (ret = read(fd, &ie, sizeof(ie)))) {
103 if (sizeof(ie) == ret) {
104 if (_ctsvc_socket_init_noti_table)
105 g_hash_table_foreach(_ctsvc_socket_init_noti_table, _ctsvc_inotify_socket_init_noti_table_foreach_cb, GINT_TO_POINTER(ie.wd));
108 __ctsvc_inotify_handle_callback(__noti_list, ie.wd, ie.mask);
110 while (0 != ie.len) {
111 ret = read(fd, name, (ie.len < sizeof(name)) ? ie.len : sizeof(name));
124 while (ret < sizeof(ie)) {
126 read_size = read(fd, name, sizeof(ie)-ret);
127 if (-1 == read_size) {
141 static inline int __ctsvc_inotify_attach_handler(int fd)
146 RETVM_IF(fd < 0, CONTACTS_ERROR_INVALID_PARAMETER, "fd is invalid");
148 channel = g_io_channel_unix_new(fd);
149 RETVM_IF(NULL == channel, CONTACTS_ERROR_SYSTEM, "System: g_io_channel_unix_new() Fail");
151 g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
153 ret = g_io_add_watch(channel, G_IO_IN, __ctsvc_inotify_gio_cb, NULL);
154 g_io_channel_unref(channel);
159 int ctsvc_inotify_init(void)
163 if (0 < __ctsvc_inoti_ref) {
165 return CONTACTS_ERROR_NONE;
167 __inoti_fd = inotify_init();
168 RETVM_IF(-1 == __inoti_fd, CONTACTS_ERROR_SYSTEM,
169 "System: inotify_init() Fail(%d)", errno);
171 ret = fcntl(__inoti_fd, F_SETFD, FD_CLOEXEC);
172 WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
173 ret = fcntl(__inoti_fd, F_SETFL, O_NONBLOCK);
174 WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
176 __inoti_handler = __ctsvc_inotify_attach_handler(__inoti_fd);
177 if (__inoti_handler <= 0) {
178 /* LCOV_EXCL_START */
179 ERR("__ctsvc_inotify_attach_handler() Fail");
183 return CONTACTS_ERROR_SYSTEM;
188 return CONTACTS_ERROR_NONE;
191 static inline int __ctsvc_inotify_get_wd(int fd, const char *notipath)
193 return inotify_add_watch(fd, notipath, IN_ACCESS);
196 static inline int __ctsvc_inotify_watch(int fd, const char *notipath)
200 ret = inotify_add_watch(fd, notipath, IN_CLOSE_WRITE);
201 RETVM_IF(-1 == ret, CONTACTS_ERROR_SYSTEM,
202 "System: inotify_add_watch() Fail(%d)", errno);
204 return CONTACTS_ERROR_NONE;
207 static int __ctsvc_noti_get_file_path(const char *view_uri, char **path)
209 ctsvc_record_type_e match = ctsvc_view_get_record_type(view_uri);
212 switch ((int)match) {
213 case CTSVC_RECORD_ADDRESSBOOK:
214 file = CTSVC_NOTI_ADDRESSBOOK_CHANGED;
216 case CTSVC_RECORD_GROUP:
217 file = CTSVC_NOTI_GROUP_CHANGED;
219 case CTSVC_RECORD_PERSON:
220 file = CTSVC_NOTI_PERSON_CHANGED;
222 case CTSVC_RECORD_CONTACT:
223 case CTSVC_RECORD_SIMPLE_CONTACT:
224 file = CTSVC_NOTI_CONTACT_CHANGED;
226 case CTSVC_RECORD_MY_PROFILE:
227 file = CTSVC_NOTI_MY_PROFILE_CHANGED;
229 case CTSVC_RECORD_NAME:
230 file = CTSVC_NOTI_NAME_CHANGED;
232 case CTSVC_RECORD_COMPANY:
233 file = CTSVC_NOTI_COMPANY_CHANGED;
235 case CTSVC_RECORD_NOTE:
236 file = CTSVC_NOTI_NOTE_CHANGED;
238 case CTSVC_RECORD_NUMBER:
239 file = CTSVC_NOTI_NUMBER_CHANGED;
241 case CTSVC_RECORD_EMAIL:
242 file = CTSVC_NOTI_EMAIL_CHANGED;
244 case CTSVC_RECORD_URL:
245 file = CTSVC_NOTI_URL_CHANGED;
247 case CTSVC_RECORD_EVENT:
248 file = CTSVC_NOTI_EVENT_CHANGED;
250 case CTSVC_RECORD_NICKNAME:
251 file = CTSVC_NOTI_NICKNAME_CHANGED;
253 case CTSVC_RECORD_ADDRESS:
254 file = CTSVC_NOTI_ADDRESS_CHANGED;
256 case CTSVC_RECORD_MESSENGER:
257 file = CTSVC_NOTI_MESSENGER_CHANGED;
259 case CTSVC_RECORD_GROUP_RELATION:
260 file = CTSVC_NOTI_GROUP_RELATION_CHANGED;
262 case CTSVC_RECORD_ACTIVITY:
263 file = CTSVC_NOTI_ACTIVITY_CHANGED;
265 case CTSVC_RECORD_ACTIVITY_PHOTO:
266 file = CTSVC_NOTI_ACTIVITY_PHOTO_CHANGED;
268 case CTSVC_RECORD_PROFILE:
269 file = CTSVC_NOTI_PROFILE_CHANGED;
271 case CTSVC_RECORD_RELATIONSHIP:
272 file = CTSVC_NOTI_RELATIONSHIP_CHANGED;
274 case CTSVC_RECORD_IMAGE:
275 file = CTSVC_NOTI_IMAGE_CHANGED;
277 case CTSVC_RECORD_EXTENSION:
278 file = CTSVC_NOTI_DATA_CHANGED;
280 case CTSVC_RECORD_PHONELOG:
281 file = CTSVC_NOTI_PHONELOG_CHANGED;
283 case CTSVC_RECORD_SPEEDDIAL:
284 file = CTSVC_NOTI_SPEEDDIAL_CHANGED;
286 case CTSVC_RECORD_SDN:
287 file = CTSVC_NOTI_SDN_CHANGED;
289 case CTSVC_RECORD_SIP:
290 file = CTSVC_NOTI_SIP_CHANGED;
292 case CTSVC_RECORD_RESULT:
294 /* LCOV_EXCL_START */
295 ERR("The type(%s) is not supported", view_uri);
296 return CONTACTS_ERROR_INVALID_PARAMETER;
300 *path = ctsvc_inotify_makepath(file);
302 ERR("ctsvc_inotify_makepath() fail");
303 return CONTACTS_ERROR_SYSTEM;
306 return CONTACTS_ERROR_NONE;
309 int ctsvc_inotify_subscribe_ipc_ready(void (*cb)(void *), void *user_data)
311 char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
312 struct socket_init_noti_info *noti_info = NULL;
314 if (NULL == noti_path) {
315 ERR("ctsvc_inotify_makepath() fail");
316 return CONTACTS_ERROR_SYSTEM;
319 if (NULL == _ctsvc_socket_init_noti_table)
320 _ctsvc_socket_init_noti_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
322 noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
324 if (NULL == noti_info) {
325 int wd = __ctsvc_inotify_get_wd(__inoti_fd, noti_path);
327 /* LCOV_EXCL_START */
328 ERR("__ctsvc_inotify_get_wd() Fail(noti_path=%s, errno : %d)", noti_path, errno);
331 return CONTACTS_ERROR_PERMISSION_DENIED;
332 return CONTACTS_ERROR_SYSTEM;
336 int ret = __ctsvc_inotify_watch(__inoti_fd, noti_path);
337 if (CONTACTS_ERROR_NONE != ret) {
338 /* LCOV_EXCL_START */
339 ERR("__ctsvc_inotify_watch() Fail");
345 noti_info = calloc(1, sizeof(struct socket_init_noti_info));
346 if (NULL == noti_info) {
347 /* LCOV_EXCL_START */
348 ERR("calloc() return NULL");
356 noti_info->cb_data = user_data;
357 g_hash_table_insert(_ctsvc_socket_init_noti_table, noti_path, noti_info);
361 noti_info->subscribe_count++;
362 return CONTACTS_ERROR_NONE;
365 int ctsvc_inotify_unsubscribe_ipc_ready()
367 RETV_IF(NULL == _ctsvc_socket_init_noti_table, CONTACTS_ERROR_INVALID_PARAMETER);
369 char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
370 struct socket_init_noti_info *noti_info = NULL;
372 if (NULL == noti_path) {
373 ERR("ctsvc_inotify_makepath() fail");
374 return CONTACTS_ERROR_SYSTEM;
377 noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
378 if (NULL == noti_info) {
379 /* LCOV_EXCL_START */
380 ERR("g_hash_table_lookup() return NULL");
382 return CONTACTS_ERROR_INVALID_PARAMETER;
386 if (1 == noti_info->subscribe_count) {
387 int wd = noti_info->wd;
388 inotify_rm_watch(__inoti_fd, wd);
389 /* free noti_info automatically */
390 g_hash_table_remove(_ctsvc_socket_init_noti_table, noti_path);
392 noti_info->subscribe_count--;
396 return CONTACTS_ERROR_NONE;
400 int ctsvc_inotify_subscribe(const char *view_uri, contacts_db_changed_cb cb, void *data)
403 noti_info *noti, *same_noti = NULL;
407 RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
408 RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
409 "__inoti_fd(%d) is invalid", __inoti_fd);
411 ret = __ctsvc_noti_get_file_path(view_uri, &path);
412 if (CONTACTS_ERROR_NONE != ret) {
413 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
417 wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
419 /* LCOV_EXCL_START */
420 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
423 return CONTACTS_ERROR_PERMISSION_DENIED;
424 return CONTACTS_ERROR_SYSTEM;
428 for (it = __noti_list; it; it = it->next) {
430 same_noti = it->data;
431 if (same_noti->wd == wd && same_noti->cb == cb
432 && STRING_EQUAL == strcmp(same_noti->view_uri, view_uri)
433 && same_noti->cb_data == data) {
442 __ctsvc_inotify_watch(__inoti_fd, path);
443 /* LCOV_EXCL_START */
444 ERR("The same callback(%s) is already exist", view_uri);
446 return CONTACTS_ERROR_INVALID_PARAMETER;
450 ret = __ctsvc_inotify_watch(__inoti_fd, path);
452 RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "__ctsvc_inotify_watch() Fail");
454 noti = calloc(1, sizeof(noti_info));
455 RETVM_IF(NULL == noti, CONTACTS_ERROR_OUT_OF_MEMORY, "calloc() Fail");
458 noti->view_uri = strdup(view_uri);
459 noti->cb_data = data;
461 noti->blocked = false;
463 __noti_list = g_slist_append(__noti_list, noti);
465 return CONTACTS_ERROR_NONE;
468 static inline int __ctsvc_del_noti(GSList **noti_list, int wd, const char *view_uri, void *cb, void *user_data)
470 int del_cnt, remain_cnt;
476 it = result = *noti_list;
478 noti_info *noti = it->data;
479 if (noti && wd == noti->wd) {
480 if (cb == noti->cb && user_data == noti->cb_data
481 && STRING_EQUAL == strcmp(noti->view_uri, view_uri)) {
483 result = g_slist_remove(result, noti);
484 free(noti->view_uri);
494 RETVM_IF(del_cnt == 0, CONTACTS_ERROR_NO_DATA, "No Data: nothing deleted, remain_cnt : %d", remain_cnt);
501 int ctsvc_inotify_unsubscribe(const char *view_uri, contacts_db_changed_cb cb, void *user_data)
506 RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
507 RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
508 "System : __inoti_fd(%d) is invalid", __inoti_fd);
510 ret = __ctsvc_noti_get_file_path(view_uri, &path);
511 if (CONTACTS_ERROR_NONE != ret) {
512 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
516 wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
518 /* LCOV_EXCL_START */
519 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
522 return CONTACTS_ERROR_PERMISSION_DENIED;
523 return CONTACTS_ERROR_SYSTEM;
527 ret = __ctsvc_del_noti(&__noti_list, wd, view_uri, cb, user_data);
528 WARN_IF(ret < CONTACTS_ERROR_NONE, "__ctsvc_del_noti() Fail(%d)", ret);
532 return inotify_rm_watch(__inoti_fd, wd);
535 ret = __ctsvc_inotify_watch(__inoti_fd, path);
540 static void __clear_nslot_list(gpointer data, gpointer user_data)
542 noti_info *noti = (noti_info *)data;
544 free(noti->view_uri);
548 static inline gboolean __ctsvc_inotify_detach_handler(guint id)
550 return g_source_remove(id);
553 void ctsvc_inotify_close(void)
555 if (1 < __ctsvc_inoti_ref) {
556 DBG("inotify ref count : %d", __ctsvc_inoti_ref);
559 } else if (__ctsvc_inoti_ref < 1) {
560 DBG("Please call connection API. inotify ref count : %d", __ctsvc_inoti_ref);
566 if (__inoti_handler) {
567 __ctsvc_inotify_detach_handler(__inoti_handler);
572 g_slist_foreach(__noti_list, __clear_nslot_list, NULL);
573 g_slist_free(__noti_list);
577 if (0 <= __inoti_fd) {
583 char* ctsvc_inotify_makepath(const char *file)
585 RETV_IF(NULL == file, NULL);
587 static int user_id_max = 10;
590 uid_t uid = getuid();
592 #ifdef _CONTACTS_IPC_CLIENT
593 if (ctsvc_client_is_in_system_session()) {
594 if (CONTACTS_ERROR_NONE != ctsvc_client_get_active_uid(&uid))
599 path_len = strlen(CTSVC_NOTI_PATH) + user_id_max + strlen(file) + 1;
600 path = calloc(1, path_len);
602 ERR("calloc() fail");
606 snprintf(path, path_len, CTSVC_NOTI_PATH"/%s", uid, file);