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>
24 #include <sys/inotify.h>
27 #include "ctsvc_internal.h"
28 #include "ctsvc_notify.h"
29 #include "ctsvc_view.h"
30 #include "ctsvc_handle.h"
31 #include "ctsvc_inotify.h"
35 #ifdef _CONTACTS_IPC_CLIENT
36 #include "ctsvc_client_ipc.h"
37 #include "ctsvc_client_utils.h"
43 contacts_db_changed_cb cb;
48 struct socket_init_noti_info {
55 static GHashTable *_ctsvc_socket_init_noti_table = NULL;
56 static int __ctsvc_inoti_ref = 0;
57 static int __inoti_fd = -1;
58 static guint __inoti_handler = 0;
59 static GSList *__noti_list = NULL;
61 static inline void __ctsvc_inotify_handle_callback(GSList *noti_list, int wd, uint32_t mask)
66 for (it = noti_list; it; it = it->next) {
67 noti = (noti_info *)it->data;
71 #ifdef _CONTACTS_IPC_CLIENT
72 if (ctsvc_ipc_is_busy()) {
78 if ((mask & IN_CLOSE_WRITE) && noti->cb) {
79 DBG("%s", noti->view_uri);
80 noti->cb(noti->view_uri, noti->cb_data);
86 static void _ctsvc_inotify_socket_init_noti_table_foreach_cb(gpointer key, gpointer value, gpointer user_data)
88 struct socket_init_noti_info *noti_info = value;
90 int wd = GPOINTER_TO_INT(user_data);
91 if (noti_info->wd == wd)
92 noti_info->cb(noti_info->cb_data);
95 static gboolean __ctsvc_inotify_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
98 struct inotify_event ie;
99 char name[FILENAME_MAX] = {0};
101 fd = g_io_channel_unix_get_fd(src);
103 while (0 < (ret = read(fd, &ie, sizeof(ie)))) {
104 if (sizeof(ie) == ret) {
105 if (_ctsvc_socket_init_noti_table)
106 g_hash_table_foreach(_ctsvc_socket_init_noti_table, _ctsvc_inotify_socket_init_noti_table_foreach_cb, GINT_TO_POINTER(ie.wd));
109 __ctsvc_inotify_handle_callback(__noti_list, ie.wd, ie.mask);
111 while (0 != ie.len) {
112 ret = read(fd, name, (ie.len < sizeof(name)) ? ie.len : sizeof(name));
125 while (ret < sizeof(ie)) {
127 read_size = read(fd, name, sizeof(ie)-ret);
128 if (-1 == read_size) {
142 static inline int __ctsvc_inotify_attach_handler(int fd)
147 RETVM_IF(fd < 0, CONTACTS_ERROR_INVALID_PARAMETER, "fd is invalid");
149 channel = g_io_channel_unix_new(fd);
150 RETVM_IF(NULL == channel, CONTACTS_ERROR_SYSTEM, "System: g_io_channel_unix_new() Fail");
152 g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL);
154 ret = g_io_add_watch(channel, G_IO_IN, __ctsvc_inotify_gio_cb, NULL);
155 g_io_channel_unref(channel);
160 int ctsvc_inotify_init(void)
164 if (0 < __ctsvc_inoti_ref) {
166 return CONTACTS_ERROR_NONE;
168 __inoti_fd = inotify_init();
169 RETVM_IF(-1 == __inoti_fd, CONTACTS_ERROR_SYSTEM,
170 "System: inotify_init() Fail(%d)", errno);
172 ret = fcntl(__inoti_fd, F_SETFD, FD_CLOEXEC);
173 WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
174 ret = fcntl(__inoti_fd, F_SETFL, O_NONBLOCK);
175 WARN_IF(ret < 0, "fcntl Fail(%d)", ret);
177 __inoti_handler = __ctsvc_inotify_attach_handler(__inoti_fd);
178 if (__inoti_handler <= 0) {
179 /* LCOV_EXCL_START */
180 ERR("__ctsvc_inotify_attach_handler() Fail");
184 return CONTACTS_ERROR_SYSTEM;
189 return CONTACTS_ERROR_NONE;
192 static inline int __ctsvc_inotify_get_wd(int fd, const char *notipath)
194 return inotify_add_watch(fd, notipath, IN_ACCESS);
197 static inline int __ctsvc_inotify_watch(int fd, const char *notipath)
201 ret = inotify_add_watch(fd, notipath, IN_CLOSE_WRITE);
202 RETVM_IF(-1 == ret, CONTACTS_ERROR_SYSTEM,
203 "System: inotify_add_watch() Fail(%d)", errno);
205 return CONTACTS_ERROR_NONE;
208 static int __ctsvc_noti_get_file_path(const char *view_uri, char **path)
210 ctsvc_record_type_e match = ctsvc_view_get_record_type(view_uri);
213 switch ((int)match) {
214 case CTSVC_RECORD_ADDRESSBOOK:
215 file = CTSVC_NOTI_ADDRESSBOOK_CHANGED;
217 case CTSVC_RECORD_GROUP:
218 file = CTSVC_NOTI_GROUP_CHANGED;
220 case CTSVC_RECORD_PERSON:
221 file = CTSVC_NOTI_PERSON_CHANGED;
223 case CTSVC_RECORD_CONTACT:
224 case CTSVC_RECORD_SIMPLE_CONTACT:
225 file = CTSVC_NOTI_CONTACT_CHANGED;
227 case CTSVC_RECORD_MY_PROFILE:
228 file = CTSVC_NOTI_MY_PROFILE_CHANGED;
230 case CTSVC_RECORD_NAME:
231 file = CTSVC_NOTI_NAME_CHANGED;
233 case CTSVC_RECORD_COMPANY:
234 file = CTSVC_NOTI_COMPANY_CHANGED;
236 case CTSVC_RECORD_NOTE:
237 file = CTSVC_NOTI_NOTE_CHANGED;
239 case CTSVC_RECORD_NUMBER:
240 file = CTSVC_NOTI_NUMBER_CHANGED;
242 case CTSVC_RECORD_EMAIL:
243 file = CTSVC_NOTI_EMAIL_CHANGED;
245 case CTSVC_RECORD_URL:
246 file = CTSVC_NOTI_URL_CHANGED;
248 case CTSVC_RECORD_EVENT:
249 file = CTSVC_NOTI_EVENT_CHANGED;
251 case CTSVC_RECORD_NICKNAME:
252 file = CTSVC_NOTI_NICKNAME_CHANGED;
254 case CTSVC_RECORD_ADDRESS:
255 file = CTSVC_NOTI_ADDRESS_CHANGED;
257 case CTSVC_RECORD_MESSENGER:
258 file = CTSVC_NOTI_MESSENGER_CHANGED;
260 case CTSVC_RECORD_GROUP_RELATION:
261 file = CTSVC_NOTI_GROUP_RELATION_CHANGED;
263 case CTSVC_RECORD_ACTIVITY:
264 file = CTSVC_NOTI_ACTIVITY_CHANGED;
266 case CTSVC_RECORD_ACTIVITY_PHOTO:
267 file = CTSVC_NOTI_ACTIVITY_PHOTO_CHANGED;
269 case CTSVC_RECORD_PROFILE:
270 file = CTSVC_NOTI_PROFILE_CHANGED;
272 case CTSVC_RECORD_RELATIONSHIP:
273 file = CTSVC_NOTI_RELATIONSHIP_CHANGED;
275 case CTSVC_RECORD_IMAGE:
276 file = CTSVC_NOTI_IMAGE_CHANGED;
278 case CTSVC_RECORD_EXTENSION:
279 file = CTSVC_NOTI_DATA_CHANGED;
281 case CTSVC_RECORD_PHONELOG:
282 file = CTSVC_NOTI_PHONELOG_CHANGED;
284 case CTSVC_RECORD_SPEEDDIAL:
285 file = CTSVC_NOTI_SPEEDDIAL_CHANGED;
287 case CTSVC_RECORD_SDN:
288 file = CTSVC_NOTI_SDN_CHANGED;
290 case CTSVC_RECORD_SIP:
291 file = CTSVC_NOTI_SIP_CHANGED;
293 case CTSVC_RECORD_RESULT:
295 /* LCOV_EXCL_START */
296 ERR("The type(%s) is not supported", view_uri);
297 return CONTACTS_ERROR_INVALID_PARAMETER;
301 *path = ctsvc_inotify_makepath(file);
303 ERR("ctsvc_inotify_makepath() fail");
304 return CONTACTS_ERROR_SYSTEM;
307 return CONTACTS_ERROR_NONE;
310 int ctsvc_inotify_subscribe_ipc_ready(void (*cb)(void *), void *user_data)
312 char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
313 struct socket_init_noti_info *noti_info = NULL;
315 if (NULL == noti_path) {
316 ERR("ctsvc_inotify_makepath() fail");
317 return CONTACTS_ERROR_SYSTEM;
320 if (NULL == _ctsvc_socket_init_noti_table)
321 _ctsvc_socket_init_noti_table = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
323 noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
325 if (NULL == noti_info) {
326 int wd = __ctsvc_inotify_get_wd(__inoti_fd, noti_path);
328 /* LCOV_EXCL_START */
329 ERR("__ctsvc_inotify_get_wd() Fail(noti_path=%s, errno : %d)", noti_path, errno);
332 return CONTACTS_ERROR_PERMISSION_DENIED;
333 return CONTACTS_ERROR_SYSTEM;
337 int ret = __ctsvc_inotify_watch(__inoti_fd, noti_path);
338 if (CONTACTS_ERROR_NONE != ret) {
339 /* LCOV_EXCL_START */
340 ERR("__ctsvc_inotify_watch() Fail");
346 noti_info = calloc(1, sizeof(struct socket_init_noti_info));
347 if (NULL == noti_info) {
348 /* LCOV_EXCL_START */
349 ERR("calloc() return NULL");
357 noti_info->cb_data = user_data;
358 g_hash_table_insert(_ctsvc_socket_init_noti_table, noti_path, noti_info);
362 noti_info->subscribe_count++;
363 return CONTACTS_ERROR_NONE;
366 int ctsvc_inotify_unsubscribe_ipc_ready()
368 RETV_IF(NULL == _ctsvc_socket_init_noti_table, CONTACTS_ERROR_INVALID_PARAMETER);
370 char *noti_path = ctsvc_inotify_makepath(CTSVC_NOTI_IPC_READY);
371 struct socket_init_noti_info *noti_info = NULL;
373 if (NULL == noti_path) {
374 ERR("ctsvc_inotify_makepath() fail");
375 return CONTACTS_ERROR_SYSTEM;
378 noti_info = g_hash_table_lookup(_ctsvc_socket_init_noti_table, noti_path);
379 if (NULL == noti_info) {
380 /* LCOV_EXCL_START */
381 ERR("g_hash_table_lookup() return NULL");
383 return CONTACTS_ERROR_INVALID_PARAMETER;
387 if (1 == noti_info->subscribe_count) {
388 int wd = noti_info->wd;
389 inotify_rm_watch(__inoti_fd, wd);
390 /* free noti_info automatically */
391 g_hash_table_remove(_ctsvc_socket_init_noti_table, noti_path);
393 noti_info->subscribe_count--;
397 return CONTACTS_ERROR_NONE;
401 int ctsvc_inotify_subscribe(const char *view_uri, contacts_db_changed_cb cb, void *data)
404 noti_info *noti, *same_noti = NULL;
408 RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
409 RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
410 "__inoti_fd(%d) is invalid", __inoti_fd);
412 ret = __ctsvc_noti_get_file_path(view_uri, &path);
413 if (CONTACTS_ERROR_NONE != ret) {
414 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
418 wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
420 /* LCOV_EXCL_START */
421 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
424 return CONTACTS_ERROR_PERMISSION_DENIED;
425 return CONTACTS_ERROR_SYSTEM;
429 for (it = __noti_list; it; it = it->next) {
431 same_noti = it->data;
432 if (same_noti->wd == wd && same_noti->cb == cb
433 && STRING_EQUAL == strcmp(same_noti->view_uri, view_uri)
434 && same_noti->cb_data == data) {
443 __ctsvc_inotify_watch(__inoti_fd, path);
444 /* LCOV_EXCL_START */
445 ERR("The same callback(%s) is already exist", view_uri);
447 return CONTACTS_ERROR_INVALID_PARAMETER;
451 ret = __ctsvc_inotify_watch(__inoti_fd, path);
453 RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "__ctsvc_inotify_watch() Fail");
455 noti = calloc(1, sizeof(noti_info));
456 RETVM_IF(NULL == noti, CONTACTS_ERROR_OUT_OF_MEMORY, "calloc() Fail");
459 noti->view_uri = strdup(view_uri);
460 noti->cb_data = data;
462 noti->blocked = false;
464 __noti_list = g_slist_append(__noti_list, noti);
466 return CONTACTS_ERROR_NONE;
469 static inline int __ctsvc_del_noti(GSList **noti_list, int wd, const char *view_uri, void *cb, void *user_data)
471 int del_cnt, remain_cnt;
477 it = result = *noti_list;
479 noti_info *noti = it->data;
480 if (noti && wd == noti->wd) {
481 if (cb == noti->cb && user_data == noti->cb_data
482 && STRING_EQUAL == strcmp(noti->view_uri, view_uri)) {
484 result = g_slist_remove(result, noti);
485 free(noti->view_uri);
495 RETVM_IF(del_cnt == 0, CONTACTS_ERROR_NO_DATA, "No Data: nothing deleted, remain_cnt : %d", remain_cnt);
502 int ctsvc_inotify_unsubscribe(const char *view_uri, contacts_db_changed_cb cb, void *user_data)
507 RETV_IF(NULL == cb, CONTACTS_ERROR_INVALID_PARAMETER);
508 RETVM_IF(__inoti_fd < 0, CONTACTS_ERROR_SYSTEM,
509 "System : __inoti_fd(%d) is invalid", __inoti_fd);
511 ret = __ctsvc_noti_get_file_path(view_uri, &path);
512 if (CONTACTS_ERROR_NONE != ret) {
513 ERR("__ctsvc_noti_get_file_path() fail(%d)", ret);
517 wd = __ctsvc_inotify_get_wd(__inoti_fd, path);
519 /* LCOV_EXCL_START */
520 ERR("__ctsvc_inotify_get_wd() Fail(errno : %d)", errno);
523 return CONTACTS_ERROR_PERMISSION_DENIED;
524 return CONTACTS_ERROR_SYSTEM;
528 ret = __ctsvc_del_noti(&__noti_list, wd, view_uri, cb, user_data);
529 WARN_IF(ret < CONTACTS_ERROR_NONE, "__ctsvc_del_noti() Fail(%d)", ret);
533 return inotify_rm_watch(__inoti_fd, wd);
536 ret = __ctsvc_inotify_watch(__inoti_fd, path);
541 static void __clear_nslot_list(gpointer data, gpointer user_data)
543 noti_info *noti = (noti_info *)data;
545 free(noti->view_uri);
549 static inline gboolean __ctsvc_inotify_detach_handler(guint id)
551 return g_source_remove(id);
554 void ctsvc_inotify_close(void)
556 if (1 < __ctsvc_inoti_ref) {
557 DBG("inotify ref count : %d", __ctsvc_inoti_ref);
560 } else if (__ctsvc_inoti_ref < 1) {
561 DBG("Please call connection API. inotify ref count : %d", __ctsvc_inoti_ref);
567 if (__inoti_handler) {
568 __ctsvc_inotify_detach_handler(__inoti_handler);
573 g_slist_foreach(__noti_list, __clear_nslot_list, NULL);
574 g_slist_free(__noti_list);
578 if (0 <= __inoti_fd) {
584 static const char* __ctsvc_inotify_get_username(uid_t uid)
587 struct passwd *pwd_result;
588 char tmp[CTSVC_STR_SHORT_LEN];
590 int ret = getpwuid_r(uid, &pwd, tmp, sizeof(tmp), &pwd_result);
591 if (ret != 0 || pwd_result == NULL) {
592 ERR("getpwuid_r() fail");
596 return SAFE_STRDUP(pwd.pw_name);
600 char* ctsvc_inotify_makepath(const char *file)
602 RETV_IF(NULL == file, NULL);
604 static int user_name_max = 32;
607 uid_t uid = getuid();
608 const char *user_name = NULL;
610 #ifdef _CONTACTS_IPC_CLIENT
611 if (ctsvc_client_is_in_system_session()) {
612 if (CONTACTS_ERROR_NONE != ctsvc_client_get_active_uid(&uid))
617 path_len = strlen(CTSVC_NOTI_PATH) + user_name_max + strlen(file) + 1;
618 path = calloc(1, path_len);
620 ERR("calloc() fail");
624 user_name = __ctsvc_inotify_get_username(uid);
626 snprintf(path, path_len, CTSVC_NOTI_PATH"/%s", user_name, file);
628 ERR("__ctsvc_inotify_get_username() fail");