Tizen 2.1 base
[platform/core/appfw/vconf.git] / vconf-kdb.c
1 /*
2  * libslp-setting
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Hakjoo Ko <hakjoo.ko@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <sys/types.h>
23 #include <sys/inotify.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <linux/version.h>
28 #include <stdlib.h>
29 #include <pthread.h>
30 #include <glib.h>
31 #include "vconf-internals.h"
32
33 #include <glib.h>
34
35 #define INOTY_EVENT_MASK   (IN_CLOSE_WRITE | IN_DELETE_SELF)
36
37 /* inotify */
38 struct noti_node {
39         int wd;
40         char *keyname;
41         vconf_callback_fn cb;
42         void *cb_data;
43         struct noti_node *next;
44 };
45 typedef struct noti_node noti_node_s;
46 static GList *g_notilist;
47
48 static int _vconf_inoti_comp_with_wd(gconstpointer a, gconstpointer b)
49 {
50         int r;
51
52         noti_node_s *key1 = (noti_node_s *) a;
53         noti_node_s *key2 = (noti_node_s *) b;
54
55         r = key1->wd - key2->wd;
56         return r;
57 }
58
59 static int _vconf_inoti_comp_with_wd_cb(gconstpointer a, gconstpointer b)
60 {
61         int r;
62
63         noti_node_s *key1 = (noti_node_s *) a;
64         noti_node_s *key2 = (noti_node_s *) b;
65
66         r = key1->wd - key2->wd;
67         if (r != 0)
68                 return r;
69
70         r = (int)(key1->cb - key2->cb);
71         return r;
72 }
73
74 static int _kdb_inoti_fd;
75
76 static pthread_mutex_t _kdb_inoti_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
77 static pthread_mutex_t _kdb_g_ns_mutex = PTHREAD_MUTEX_INITIALIZER;
78
79 static GSource *_kdb_handler;
80
81 static GList* _vconf_copy_noti_list(GList *orig_notilist)
82 {
83         GList *copy_notilist = NULL;
84         struct noti_node *n = NULL;
85         struct noti_node *t = NULL;
86
87         if (!orig_notilist)
88                 return NULL;
89
90         orig_notilist = g_list_first(orig_notilist);
91         if (!orig_notilist)
92                 return NULL;
93
94         while(orig_notilist) {
95                 t = orig_notilist->data;
96
97                 n = calloc(1, sizeof(noti_node_s));
98                 if (n == NULL) {
99                         ERR("_vconf_copy_noti_list : calloc failed. memory full");
100                         break;
101                 }
102
103                 n->wd = t->wd;
104                 n->keyname = strndup(t->keyname, BUF_LEN);
105                 n->cb_data = t->cb_data;
106                 n->cb = t->cb;
107
108                 copy_notilist = g_list_append(copy_notilist, n);
109                 orig_notilist = g_list_next(orig_notilist);
110         }
111         return copy_notilist;
112
113 }
114
115 static void _vconf_free_noti_node(gpointer data)
116 {
117         struct noti_node *n = (struct noti_node*)data;
118         g_free(n->keyname);
119         g_free(n);
120 }
121
122 static void _vconf_free_noti_list(GList *noti_list)
123 {
124         g_list_free_full(noti_list, _vconf_free_noti_node);
125 }
126
127
128 static gboolean _vconf_kdb_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
129 {
130         int fd, r;
131         struct inotify_event ie;
132         GList *l_notilist = NULL;
133
134         INFO("vconf noti function");
135
136         fd = g_io_channel_unix_get_fd(src);
137         r = read(fd, &ie, sizeof(ie));
138
139         while (r > 0) {
140                 INFO("read event from GIOChannel. pid : %d", getpid());
141
142                 pthread_mutex_lock(&_kdb_g_ns_mutex);
143                 l_notilist = _vconf_copy_noti_list(g_notilist);
144                 pthread_mutex_unlock(&_kdb_g_ns_mutex);
145
146
147                 if (l_notilist) {
148
149                         struct noti_node *t = NULL;
150                         GList *noti_list = NULL;
151                         keynode_t* keynode = NULL;
152
153                         retvm_if(!(ie.mask & INOTY_EVENT_MASK), TRUE,
154                                 "Invalid argument: ie.mask(%d), ie.len(%d)",
155                                  ie.mask, ie.len);
156
157                         noti_list = g_list_first(l_notilist);
158
159                         while (noti_list) {
160                                 t = noti_list->data;
161
162                                 keynode_t* keynode = _vconf_keynode_new();
163                                 retvm_if(keynode == NULL, TRUE, "key malloc fail");
164
165                                 if( (t) && (t->wd == ie.wd) ) {
166                                         if ((ie.mask & IN_DELETE_SELF)) {
167                                                 INFO("Notify that key(%s) is deleted", t->keyname);
168                                                 _vconf_keynode_set_keyname(keynode, (const char *)t->keyname);
169                                                 _vconf_keynode_set_null(keynode);
170                                                 t->cb(keynode, t->cb_data);
171                                                 _vconf_kdb_del_notify(t->keyname, t->cb);
172                                         } else {
173                                                 _vconf_keynode_set_keyname(keynode, t->keyname);
174                                                 _vconf_get_key(keynode);
175                                                 t->cb(keynode, t->cb_data);
176                                         }
177                                 }
178
179                                 _vconf_keynode_free(keynode);
180
181                                 noti_list = g_list_next(noti_list);
182                         }
183
184                         _vconf_free_noti_list(l_notilist);
185                 }
186
187                 if (ie.len > 0)
188                         (void) lseek(fd, ie.len, SEEK_CUR);
189
190                 r = read(fd, &ie, sizeof(ie));
191         }
192         return TRUE;
193 }
194
195 static int _vconf_kdb_noti_init(void)
196 {
197         GIOChannel *gio;
198         int ret = 0;
199
200         pthread_mutex_lock(&_kdb_inoti_fd_mutex);
201
202         if (0 < _kdb_inoti_fd) {
203                 ERR("Error: invalid _kdb_inoti_fd");
204                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
205                 return VCONF_ERROR;
206         }
207         _kdb_inoti_fd = inotify_init();
208         if (_kdb_inoti_fd == -1) {
209                 char err_buf[100] = { 0, };
210                 strerror_r(errno, err_buf, sizeof(err_buf));
211                 ERR("inotify init: %s", err_buf);
212                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
213                 return VCONF_ERROR;
214         }
215
216         ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC);
217         if (ret < 0) {
218                 char err_buf[100] = { 0, };
219                 strerror_r(errno, err_buf, sizeof(err_buf));
220                 ERR("inotify init: %s", err_buf);
221                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
222                 return VCONF_ERROR;
223         }
224
225         ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK);
226         if (ret < 0) {
227                 char err_buf[100] = { 0, };
228                 strerror_r(errno, err_buf, sizeof(err_buf));
229                 ERR("inotify init: %s", err_buf);
230                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
231                 return VCONF_ERROR;
232         }
233
234         pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
235
236         gio = g_io_channel_unix_new(_kdb_inoti_fd);
237         retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
238
239         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
240
241         _kdb_handler = g_io_create_watch(gio, G_IO_IN);
242         g_source_set_callback(_kdb_handler, (GSourceFunc) _vconf_kdb_gio_cb, NULL, NULL);
243         g_source_attach(_kdb_handler, NULL);
244         g_io_channel_unref(gio);
245         g_source_unref(_kdb_handler);
246
247         return VCONF_OK;
248 }
249
250         int
251 _vconf_kdb_add_notify(const char *keyname, vconf_callback_fn cb, void *data)
252 {
253         char path[KEY_PATH];
254         int wd, r;
255         struct noti_node t, *n;
256         char err_buf[ERR_LEN] = { 0, };
257         int ret = 0;
258         GList *list = NULL;
259         int func_ret = VCONF_OK;
260
261         retvm_if((keyname == NULL || cb == NULL), VCONF_ERROR,
262                         "_vconf_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)",
263                         keyname, cb);
264
265         if (_kdb_inoti_fd <= 0)
266                 if (_vconf_kdb_noti_init())
267                         return VCONF_ERROR;
268
269         ret = _vconf_get_key_path((char*)keyname, path);
270         retvm_if(ret != VCONF_OK, VCONF_ERROR, "Invalid argument: key is not valid");
271
272         if (0 != access(path, F_OK)) {
273                 if (errno == ENOENT) {
274                         ERR("_vconf_kdb_add_notify : Key(%s) does not exist", keyname);
275                         return VCONF_ERROR;
276                 }
277         }
278
279         wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
280         if (wd == -1) {
281                 strerror_r(errno, err_buf, sizeof(err_buf));
282                 ERR("_vconf_kdb_add_notify : add noti(%s)", err_buf);
283                 return VCONF_ERROR;
284         }
285
286         t.wd = wd;
287         t.cb = cb;
288
289         pthread_mutex_lock(&_kdb_g_ns_mutex);
290
291         list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_vconf_inoti_comp_with_wd_cb);
292         if (list) {
293                 ERR("_vconf_kdb_add_notify : key(%s) has same callback(%p)", keyname, cb);
294                 errno = EALREADY;
295                 func_ret = VCONF_ERROR;
296                 goto out_func;
297         }
298
299         n = calloc(1, sizeof(noti_node_s));
300         if (n == NULL) {
301                 strerror_r(errno, err_buf, sizeof(err_buf));
302                 ERR("_vconf_kdb_add_notify : add noti(%s)", err_buf);
303                 func_ret = VCONF_ERROR;
304                 goto out_func;
305         }
306
307         n->wd = wd;
308         n->keyname = strndup(keyname, BUF_LEN);
309         n->cb_data = data;
310         n->cb = cb;
311
312         g_notilist = g_list_append(g_notilist, n);
313         if(!g_notilist) {
314                 ERR("g_list_append fail");
315         }
316
317         INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist));
318
319 out_func:
320         pthread_mutex_unlock(&_kdb_g_ns_mutex);
321
322         return func_ret;
323 }
324
325         int
326 _vconf_kdb_del_notify(const char *keyname, vconf_callback_fn cb)
327 {
328         int wd = 0;
329         int r = 0;
330         struct noti_node *n = NULL;
331         struct noti_node t;
332         char path[KEY_PATH] = { 0, };
333         char err_buf[ERR_LEN] = { 0, };
334         int del = 0;
335         int remain = 0;
336         int ret = 0;
337         int func_ret = VCONF_OK;
338         GList *noti_list;
339
340         retvm_if(keyname == NULL, VCONF_ERROR, "Invalid argument: keyname(%s)", keyname);
341         retvm_if(_kdb_inoti_fd == 0, VCONF_ERROR, "Invalid operation: not exist anything for inotify");
342
343         ret = _vconf_get_key_path((char*)keyname, path);
344         retvm_if(ret != VCONF_OK, VCONF_ERROR, "Invalid argument: key is not valid");
345
346         /* get wd */
347         wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
348         if (wd == -1) {
349                 strerror_r(errno, err_buf, sizeof(err_buf));
350                 ERR("Error: inotify_add_watch() [%s]: %s", path, err_buf);
351                 return VCONF_ERROR;
352         }
353
354         pthread_mutex_lock(&_kdb_g_ns_mutex);
355
356         t.wd = wd;
357         t.cb = cb;
358
359         noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_vconf_inoti_comp_with_wd_cb);
360         if(noti_list) {
361                 del++;
362
363                 n = noti_list->data;
364                 g_notilist = g_list_remove(g_notilist, n);
365                 g_free(n->keyname);
366                 g_free(n);
367
368                 INFO("key(%s) cb is removed. remained noti list total length(%d)",
369                                 keyname, g_list_length(g_notilist));
370         }
371
372         noti_list = NULL;
373         noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_vconf_inoti_comp_with_wd);
374         if(noti_list == NULL) {
375                 INFO("all noti for keyname(%s)/wd(%d) is removed", keyname, wd);
376
377                 r = inotify_rm_watch(_kdb_inoti_fd, wd);
378                 if(r == -1) {
379                         strerror_r(errno, err_buf, sizeof(err_buf));
380                         ERR("Error: inotify_rm_watch [%s]: %s", keyname, err_buf);
381                         func_ret = VCONF_ERROR;
382                 }
383         }
384
385         if(g_list_length(g_notilist) == 0) {
386                 close(_kdb_inoti_fd);
387                 _kdb_inoti_fd = 0;
388
389                 g_source_destroy(_kdb_handler);
390                 _kdb_handler = NULL;
391
392                 g_list_free(g_notilist);
393                 g_notilist = NULL;
394
395                 INFO("all noti list is freed");
396         }
397
398         pthread_mutex_unlock(&_kdb_g_ns_mutex);
399
400         if(del == 0) {
401                 ERR("Error: nothing deleted");
402                 errno = ENOENT;
403                 func_ret = VCONF_ERROR;
404         }
405
406         return func_ret;
407 }