2 * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <sys/types.h>
18 #include <sys/inotify.h>
22 #include <linux/version.h>
27 #include <app_preference.h>
28 #include <app_preference_internal.h>
32 #define INOTY_EVENT_MASK (IN_CLOSE_WRITE | IN_DELETE_SELF)
38 preference_changed_cb cb;
40 struct noti_node *next;
42 typedef struct noti_node noti_node_s;
43 static GList *g_notilist;
45 static int _preference_inoti_comp_with_wd(gconstpointer a, gconstpointer b)
49 noti_node_s *key1 = (noti_node_s *) a;
50 noti_node_s *key2 = (noti_node_s *) b;
52 r = key1->wd - key2->wd;
56 static int _kdb_inoti_fd;
58 static pthread_mutex_t _kdb_inoti_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
59 static pthread_mutex_t _kdb_g_ns_mutex = PTHREAD_MUTEX_INITIALIZER;
61 static GSource *_kdb_handler;
63 static GList *_preference_copy_noti_list(GList *orig_notilist)
65 GList *copy_notilist = NULL;
66 struct noti_node *n = NULL;
67 struct noti_node *t = NULL;
68 char err_buf[ERR_LEN] = {0,};
73 orig_notilist = g_list_first(orig_notilist);
77 while (orig_notilist) {
79 t = orig_notilist->data;
81 WARN("noti item data is null");
85 if ((t->keyname == NULL) || (strlen(t->keyname) == 0)) {
86 WARN("noti item data key name is null");
90 n = calloc(1, sizeof(noti_node_s));
92 ERR("_preference_copy_noti_list : calloc failed. memory full");
96 n->keyname = strndup(t->keyname, PREFERENCE_KEY_PATH_LEN);
97 if (n->keyname == NULL) {
99 strerror_r(errno, err_buf, sizeof(err_buf));
100 ERR("The memory is insufficient, errno: %d (%s)", errno, err_buf);
106 n->cb_data = t->cb_data;
109 copy_notilist = g_list_append(copy_notilist, n);
112 orig_notilist = g_list_next(orig_notilist);
114 return copy_notilist;
117 static void _preference_free_noti_node(gpointer data)
119 struct noti_node *n = (struct noti_node *)data;
124 static void _preference_free_noti_list(GList *noti_list)
126 g_list_free_full(noti_list, _preference_free_noti_node);
130 static gboolean _preference_kdb_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
133 struct inotify_event ie;
134 GList *l_notilist = NULL;
135 struct noti_node *t = NULL;
136 GList *noti_list = NULL;
139 fd = g_io_channel_unix_get_fd(src);
140 r = read(fd, &ie, sizeof(ie));
143 if (ie.mask & INOTY_EVENT_MASK) {
145 INFO("read event from GIOChannel. wd : %d", ie.wd);
147 pthread_mutex_lock(&_kdb_g_ns_mutex);
148 l_notilist = _preference_copy_noti_list(g_notilist);
149 pthread_mutex_unlock(&_kdb_g_ns_mutex);
152 noti_list = g_list_first(l_notilist);
156 keynode = _preference_keynode_new();
157 if (keynode == NULL) {
158 ERR("key malloc fail");
162 if ((t) && (t->wd == ie.wd) && (t->keyname)) {
163 res = _preference_keynode_set_keyname(keynode, t->keyname);
164 if (res != PREFERENCE_ERROR_NONE) {
165 ERR("_preference_keynode_set_keyname() failed(%d)", res);
169 if ((ie.mask & IN_DELETE_SELF)) {
170 res = _preference_kdb_del_notify(keynode);
171 if (res != PREFERENCE_ERROR_NONE)
172 ERR("_preference_kdb_del_notify() failed(%d)", res);
174 res = _preference_get_key(keynode);
175 if (res != PREFERENCE_ERROR_NONE)
176 ERR("_preference_get_key() failed(%d)", res);
178 INFO("key(%s) is changed. cb(%p) called", t->keyname, t->cb);
179 t->cb(t->keyname, t->cb_data);
181 } else if ((t) && (t->keyname == NULL)) { /* for debugging */
182 ERR("preference keyname is null.");
185 _preference_keynode_free(keynode);
187 noti_list = g_list_next(noti_list);
190 _preference_free_noti_list(l_notilist);
195 (void) lseek(fd, ie.len, SEEK_CUR);
197 r = read(fd, &ie, sizeof(ie));
202 static int _preference_kdb_noti_init(void)
206 char err_buf[ERR_LEN] = { 0, };
208 pthread_mutex_lock(&_kdb_inoti_fd_mutex);
210 if (0 < _kdb_inoti_fd) {
211 /* LCOV_EXCL_START */
212 ERR("Error: invalid _kdb_inoti_fd");
213 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
214 return PREFERENCE_ERROR_IO_ERROR;
217 _kdb_inoti_fd = inotify_init();
218 if (_kdb_inoti_fd == -1) {
219 /* LCOV_EXCL_START */
220 strerror_r(errno, err_buf, sizeof(err_buf));
221 ERR("inotify init: %s", err_buf);
222 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
223 return PREFERENCE_ERROR_IO_ERROR;
227 ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC);
229 /* LCOV_EXCL_START */
230 strerror_r(errno, err_buf, sizeof(err_buf));
231 ERR("inotify init: %s", err_buf);
232 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
233 return PREFERENCE_ERROR_IO_ERROR;
237 ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK);
239 /* LCOV_EXCL_START */
240 strerror_r(errno, err_buf, sizeof(err_buf));
241 ERR("inotify init: %s", err_buf);
242 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
243 return PREFERENCE_ERROR_IO_ERROR;
247 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
249 gio = g_io_channel_unix_new(_kdb_inoti_fd);
250 retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
252 g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
254 _kdb_handler = g_io_create_watch(gio, G_IO_IN);
255 g_source_set_callback(_kdb_handler, (GSourceFunc) _preference_kdb_gio_cb, NULL, NULL);
256 g_source_attach(_kdb_handler, NULL);
257 g_io_channel_unref(gio);
258 g_source_unref(_kdb_handler);
260 return PREFERENCE_ERROR_NONE;
263 int _preference_kdb_add_notify(keynode_t *keynode, preference_changed_cb cb, void *data)
267 struct noti_node t, *n, *node;
268 char err_buf[ERR_LEN] = { 0, };
271 int func_ret = PREFERENCE_ERROR_NONE;
272 char *keyname = keynode->keyname;
274 retvm_if((keyname == NULL || cb == NULL), PREFERENCE_ERROR_INVALID_PARAMETER,
275 "_preference_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)",
278 if (_kdb_inoti_fd <= 0)
279 if (_preference_kdb_noti_init())
280 return PREFERENCE_ERROR_IO_ERROR;
282 ret = _preference_get_key_path(keynode, path);
283 if (ret != PREFERENCE_ERROR_NONE) {
284 ERR("Invalid argument: key is not valid");
285 return PREFERENCE_ERROR_INVALID_PARAMETER;
288 if (access(path, F_OK) != 0) {
289 if (errno == ENOENT) {
290 ERR("_preference_kdb_add_notify : Key(%s) does not exist", keyname);
291 return PREFERENCE_ERROR_IO_ERROR;
295 wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
297 strerror_r(errno, err_buf, sizeof(err_buf));
298 ERR("_preference_kdb_add_notify : add noti(%s)", err_buf);
299 return PREFERENCE_ERROR_IO_ERROR;
304 pthread_mutex_lock(&_kdb_g_ns_mutex);
306 list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
308 /* LCOV_EXCL_START */
309 WARN("_preference_kdb_add_notify : key(%s) change callback(%p)", keyname, cb);
313 node->cb_data = data;
320 n = calloc(1, sizeof(noti_node_s));
322 /* LCOV_EXCL_START */
323 strerror_r(errno, err_buf, sizeof(err_buf));
324 ERR("_preference_kdb_add_notify : add noti(%s)", err_buf);
325 func_ret = PREFERENCE_ERROR_IO_ERROR;
330 n->keyname = strndup(keyname, PREFERENCE_KEY_PATH_LEN);
331 if (n->keyname == NULL) {
332 /* LCOV_EXCL_START */
333 strerror_r(errno, err_buf, sizeof(err_buf));
334 ERR("The memory is insufficient, errno: %d (%s)", errno, err_buf);
343 g_notilist = g_list_append(g_notilist, n);
345 ERR("g_list_append fail");
347 INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist));
350 pthread_mutex_unlock(&_kdb_g_ns_mutex);
355 int _preference_kdb_del_notify(keynode_t *keynode)
359 struct noti_node *n = NULL;
361 char path[PATH_MAX] = { 0, };
362 char err_buf[ERR_LEN] = { 0, };
365 char *keyname = keynode->keyname;
366 int func_ret = PREFERENCE_ERROR_NONE;
369 retvm_if(keyname == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: keyname(%s)", keyname);
371 ret = _preference_get_key_path(keynode, path);
372 if (ret != PREFERENCE_ERROR_NONE) {
373 ERR("Invalid argument: key is not valid");
374 return PREFERENCE_ERROR_INVALID_PARAMETER;
377 if (access(path, F_OK) != 0) {
378 if (errno == ENOENT) {
379 ERR("_preference_kdb_del_notify : Key(%s) does not exist", keyname);
380 return PREFERENCE_ERROR_IO_ERROR;
384 retvm_if(_kdb_inoti_fd == 0, PREFERENCE_ERROR_NONE, "Invalid operation: not exist anything for inotify");
387 wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
389 strerror_r(errno, err_buf, sizeof(err_buf));
390 ERR("Error: inotify_add_watch() [%s]: %s", path, err_buf);
391 return PREFERENCE_ERROR_IO_ERROR;
394 pthread_mutex_lock(&_kdb_g_ns_mutex);
398 noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
403 g_notilist = g_list_remove(g_notilist, n);
407 r = inotify_rm_watch(_kdb_inoti_fd, wd);
409 strerror_r(errno, err_buf, sizeof(err_buf));
410 ERR("Error: inotify_rm_watch [%s]: %s", keyname, err_buf);
411 func_ret = PREFERENCE_ERROR_IO_ERROR;
414 INFO("key(%s) cb is removed. remained noti list total length(%d)",
415 keyname, g_list_length(g_notilist));
418 if (g_list_length(g_notilist) == 0) {
419 close(_kdb_inoti_fd);
422 g_source_destroy(_kdb_handler);
425 g_list_free(g_notilist);
428 INFO("all noti list is freed");
431 pthread_mutex_unlock(&_kdb_g_ns_mutex);
435 func_ret = PREFERENCE_ERROR_IO_ERROR;