merge with master
[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, VCONF_KEY_PATH_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
152                         if (!(ie.mask & INOTY_EVENT_MASK)) {
153                                 INFO("Invalid argument: ie.mask(%d), ie.len(%d)", ie.mask, ie.len);
154                                 return TRUE;
155                         }
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                                                 INFO("key(%s) is changed. cb called", t->keyname);
177                                         }
178                                 }
179
180                                 _vconf_keynode_free(keynode);
181
182                                 noti_list = g_list_next(noti_list);
183                         }
184
185                         _vconf_free_noti_list(l_notilist);
186                 }
187
188                 if (ie.len > 0)
189                         (void) lseek(fd, ie.len, SEEK_CUR);
190
191                 r = read(fd, &ie, sizeof(ie));
192         }
193         return TRUE;
194 }
195
196 static int _vconf_kdb_noti_init(void)
197 {
198         GIOChannel *gio;
199         int ret = 0;
200
201         pthread_mutex_lock(&_kdb_inoti_fd_mutex);
202
203         if (0 < _kdb_inoti_fd) {
204                 ERR("Error: invalid _kdb_inoti_fd");
205                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
206                 return VCONF_ERROR;
207         }
208         _kdb_inoti_fd = inotify_init();
209         if (_kdb_inoti_fd == -1) {
210                 char err_buf[100] = { 0, };
211                 strerror_r(errno, err_buf, sizeof(err_buf));
212                 ERR("inotify init: %s", err_buf);
213                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
214                 return VCONF_ERROR;
215         }
216
217         ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC);
218         if (ret < 0) {
219                 char err_buf[100] = { 0, };
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 VCONF_ERROR;
224         }
225
226         ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK);
227         if (ret < 0) {
228                 char err_buf[100] = { 0, };
229                 strerror_r(errno, err_buf, sizeof(err_buf));
230                 ERR("inotify init: %s", err_buf);
231                 pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
232                 return VCONF_ERROR;
233         }
234
235         pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
236
237         gio = g_io_channel_unix_new(_kdb_inoti_fd);
238         retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
239
240         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
241
242         _kdb_handler = g_io_create_watch(gio, G_IO_IN);
243         g_source_set_callback(_kdb_handler, (GSourceFunc) _vconf_kdb_gio_cb, NULL, NULL);
244         g_source_attach(_kdb_handler, NULL);
245         g_io_channel_unref(gio);
246         g_source_unref(_kdb_handler);
247
248         return VCONF_OK;
249 }
250
251         int
252 _vconf_kdb_add_notify(const char *keyname, vconf_callback_fn cb, void *data)
253 {
254         char path[VCONF_KEY_PATH_LEN];
255         int wd;
256         struct noti_node t, *n;
257         char err_buf[ERR_LEN] = { 0, };
258         int ret = 0;
259         GList *list = NULL;
260         int func_ret = VCONF_OK;
261
262         retvm_if((keyname == NULL || cb == NULL), VCONF_ERROR,
263                         "_vconf_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)",
264                         keyname, cb);
265
266         if (_kdb_inoti_fd <= 0)
267                 if (_vconf_kdb_noti_init())
268                         return VCONF_ERROR;
269
270         ret = _vconf_get_key_path((char*)keyname, path);
271         retvm_if(ret != VCONF_OK, VCONF_ERROR, "Invalid argument: key is not valid");
272
273         if (0 != access(path, F_OK)) {
274                 if (errno == ENOENT) {
275                         ERR("_vconf_kdb_add_notify : Key(%s) does not exist", keyname);
276                         return VCONF_ERROR;
277                 }
278         }
279
280         wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
281         if (wd == -1) {
282                 strerror_r(errno, err_buf, sizeof(err_buf));
283                 ERR("_vconf_kdb_add_notify : add noti(%s)", err_buf);
284                 return VCONF_ERROR;
285         }
286
287         t.wd = wd;
288         t.cb = cb;
289
290         pthread_mutex_lock(&_kdb_g_ns_mutex);
291
292         list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_vconf_inoti_comp_with_wd_cb);
293         if (list) {
294                 ERR("_vconf_kdb_add_notify : key(%s) has same callback(%p)", keyname, cb);
295                 errno = EALREADY;
296                 func_ret = VCONF_ERROR;
297                 goto out_func;
298         }
299
300         n = calloc(1, sizeof(noti_node_s));
301         if (n == NULL) {
302                 strerror_r(errno, err_buf, sizeof(err_buf));
303                 ERR("_vconf_kdb_add_notify : add noti(%s)", err_buf);
304                 func_ret = VCONF_ERROR;
305                 goto out_func;
306         }
307
308         n->wd = wd;
309         n->keyname = strndup(keyname, VCONF_KEY_PATH_LEN);
310         n->cb_data = data;
311         n->cb = cb;
312
313         g_notilist = g_list_append(g_notilist, n);
314         if(!g_notilist) {
315                 ERR("g_list_append fail");
316         }
317
318         INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist));
319
320 out_func:
321         pthread_mutex_unlock(&_kdb_g_ns_mutex);
322
323         return func_ret;
324 }
325
326         int
327 _vconf_kdb_del_notify(const char *keyname, vconf_callback_fn cb)
328 {
329         int wd = 0;
330         int r = 0;
331         struct noti_node *n = NULL;
332         struct noti_node t;
333         char path[VCONF_KEY_PATH_LEN] = { 0, };
334         char err_buf[ERR_LEN] = { 0, };
335         int del = 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 }