Add an exception handling about args
[platform/core/api/application.git] / preference / preference_inoti.c
1 /*
2  * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <sys/types.h>
18 #include <sys/inotify.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <linux/version.h>
23 #include <stdlib.h>
24 #include <pthread.h>
25 #include <glib.h>
26
27 #include <app_preference.h>
28 #include <app_preference_internal.h>
29
30 #include <glib.h>
31
32 #define INOTY_EVENT_MASK   (IN_CLOSE_WRITE | IN_DELETE_SELF)
33
34 /* inotify */
35 struct noti_node {
36         int wd;
37         char *keyname;
38         preference_changed_cb cb;
39         void *cb_data;
40         struct noti_node *next;
41 };
42 typedef struct noti_node noti_node_s;
43 static GList *g_notilist;
44
45 static int _preference_inoti_comp_with_wd(gconstpointer a, gconstpointer b)
46 {
47         int r;
48
49         noti_node_s *key1 = (noti_node_s *) a;
50         noti_node_s *key2 = (noti_node_s *) b;
51
52         r = key1->wd - key2->wd;
53         return r;
54 }
55
56 static int _kdb_inoti_fd;
57
58 static pthread_mutex_t _kdb_inoti_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
59 static pthread_mutex_t _kdb_g_ns_mutex = PTHREAD_MUTEX_INITIALIZER;
60
61 static GSource *_kdb_handler;
62
63 static GList *_preference_copy_noti_list(GList *orig_notilist)
64 {
65         GList *copy_notilist = NULL;
66         struct noti_node *n = NULL;
67         struct noti_node *t = NULL;
68         char err_buf[ERR_LEN] = {0,};
69
70         if (!orig_notilist)
71                 return NULL;
72
73         orig_notilist = g_list_first(orig_notilist);
74         if (!orig_notilist)
75                 return NULL;
76
77         while (orig_notilist) {
78                 do {
79                         t = orig_notilist->data;
80                         if (t == NULL) {
81                                 WARN("noti item data is null");
82                                 break;
83                         }
84
85                         if ((t->keyname == NULL) || (strlen(t->keyname) == 0)) {
86                                 WARN("noti item data key name is null");
87                                 break;
88                         }
89
90                         n = calloc(1, sizeof(noti_node_s));
91                         if (n == NULL) {
92                                 ERR("_preference_copy_noti_list : calloc failed. memory full");
93                                 break;
94                         }
95
96                         n->keyname = strndup(t->keyname, PREFERENCE_KEY_PATH_LEN);
97                         if (n->keyname == NULL) {
98                                 /* LCOV_EXCL_START */
99                                 strerror_r(errno, err_buf, sizeof(err_buf));
100                                 ERR("The memory is insufficient, errno: %d (%s)", errno, err_buf);
101                                 free(n);
102                                 break;
103                                 /* LCOV_EXCL_STOP */
104                         }
105                         n->wd = t->wd;
106                         n->cb_data = t->cb_data;
107                         n->cb = t->cb;
108
109                         copy_notilist = g_list_append(copy_notilist, n);
110                 } while (0);
111
112                 orig_notilist = g_list_next(orig_notilist);
113         }
114         return copy_notilist;
115 }
116
117 static void _preference_free_noti_node(gpointer data)
118 {
119         struct noti_node *n = (struct noti_node *)data;
120         g_free(n->keyname);
121         g_free(n);
122 }
123
124 static void _preference_free_noti_list(GList *noti_list)
125 {
126         g_list_free_full(noti_list, _preference_free_noti_node);
127 }
128
129
130 static gboolean _preference_kdb_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
131 {
132         int fd, r, res;
133         struct inotify_event ie;
134         GList *l_notilist = NULL;
135         struct noti_node *t = NULL;
136         GList *noti_list = NULL;
137         keynode_t *keynode;
138
139         fd = g_io_channel_unix_get_fd(src);
140         r = read(fd, &ie, sizeof(ie));
141
142         while (r > 0) {
143                 if (ie.mask & INOTY_EVENT_MASK) {
144
145                         INFO("read event from GIOChannel. wd : %d", ie.wd);
146
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);
150
151                         if (l_notilist) {
152                                 noti_list = g_list_first(l_notilist);
153                                 while (noti_list) {
154                                         t = noti_list->data;
155
156                                         keynode = _preference_keynode_new();
157                                         if (keynode == NULL) {
158                                                 ERR("key malloc fail");
159                                                 break;
160                                         }
161
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);
166                                                         goto out_func;
167                                                 }
168
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);
173                                                 } else {
174                                                         res = _preference_get_key(keynode);
175                                                         if (res != PREFERENCE_ERROR_NONE)
176                                                                 ERR("_preference_get_key() failed(%d)", res);
177
178                                                         INFO("key(%s) is changed. cb(%p) called", t->keyname, t->cb);
179                                                         t->cb(t->keyname, t->cb_data);
180                                                 }
181                                         } else if ((t) && (t->keyname == NULL)) { /* for debugging */
182                                                 ERR("preference keyname is null.");
183                                         }
184 out_func:
185                                         _preference_keynode_free(keynode);
186
187                                         noti_list = g_list_next(noti_list);
188                                 }
189
190                                 _preference_free_noti_list(l_notilist);
191                         }
192                 }
193
194                 if (ie.len > 0)
195                         (void) lseek(fd, ie.len, SEEK_CUR);
196
197                 r = read(fd, &ie, sizeof(ie));
198         }
199         return TRUE;
200 }
201
202 static int _preference_kdb_noti_init(void)
203 {
204         GIOChannel *gio;
205         int ret = 0;
206         char err_buf[ERR_LEN] = { 0, };
207
208         pthread_mutex_lock(&_kdb_inoti_fd_mutex);
209
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;
215                 /* LCOV_EXCL_STOP */
216         }
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;
224                 /* LCOV_EXCL_STOP */
225         }
226
227         ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC);
228         if (ret < 0) {
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;
234                 /* LCOV_EXCL_STOP */
235         }
236
237         ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK);
238         if (ret < 0) {
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;
244                 /* LCOV_EXCL_STOP */
245         }
246
247         pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
248
249         gio = g_io_channel_unix_new(_kdb_inoti_fd);
250         retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
251
252         g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
253
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);
259
260         return PREFERENCE_ERROR_NONE;
261 }
262
263 int _preference_kdb_add_notify(keynode_t *keynode, preference_changed_cb cb, void *data)
264 {
265         char path[PATH_MAX];
266         int wd;
267         struct noti_node t, *n, *node;
268         char err_buf[ERR_LEN] = { 0, };
269         int ret = 0;
270         GList *list = NULL;
271         int func_ret = PREFERENCE_ERROR_NONE;
272         char *keyname = keynode->keyname;
273
274         retvm_if((keyname == NULL || cb == NULL), PREFERENCE_ERROR_INVALID_PARAMETER,
275                         "_preference_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)",
276                         keyname, cb);
277
278         if (_kdb_inoti_fd <= 0)
279                 if (_preference_kdb_noti_init())
280                         return PREFERENCE_ERROR_IO_ERROR;
281
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;
286         }
287
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;
292                 }
293         }
294
295         wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
296         if (wd == -1) {
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;
300         }
301
302         t.wd = wd;
303
304         pthread_mutex_lock(&_kdb_g_ns_mutex);
305
306         list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
307         if (list) {
308                 /* LCOV_EXCL_START */
309                 WARN("_preference_kdb_add_notify : key(%s) change callback(%p)", keyname, cb);
310
311                 node = list->data;
312                 node->wd = wd;
313                 node->cb_data = data;
314                 node->cb = cb;
315
316                 goto out_func;
317                 /* LCOV_EXCL_STOP */
318         }
319
320         n = calloc(1, sizeof(noti_node_s));
321         if (n == NULL) {
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;
326                 goto out_func;
327                 /* LCOV_EXCL_STOP */
328         }
329
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);
335                 free(n);
336                 goto out_func;
337                 /* LCOV_EXCL_STOP */
338         }
339         n->wd = wd;
340         n->cb_data = data;
341         n->cb = cb;
342
343         g_notilist = g_list_append(g_notilist, n);
344         if (!g_notilist)
345                 ERR("g_list_append fail");
346
347         INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist));
348
349 out_func:
350         pthread_mutex_unlock(&_kdb_g_ns_mutex);
351
352         return func_ret;
353 }
354
355 int _preference_kdb_del_notify(keynode_t *keynode)
356 {
357         int wd = 0;
358         int r = 0;
359         struct noti_node *n = NULL;
360         struct noti_node t;
361         char path[PATH_MAX] = { 0, };
362         char err_buf[ERR_LEN] = { 0, };
363         int del = 0;
364         int ret = 0;
365         char *keyname = keynode->keyname;
366         int func_ret = PREFERENCE_ERROR_NONE;
367         GList *noti_list;
368
369         retvm_if(keyname == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: keyname(%s)", keyname);
370
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;
375         }
376
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;
381                 }
382         }
383
384         retvm_if(_kdb_inoti_fd == 0, PREFERENCE_ERROR_NONE, "Invalid operation: not exist anything for inotify");
385
386         /* get wd */
387         wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
388         if (wd == -1) {
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;
392         }
393
394         pthread_mutex_lock(&_kdb_g_ns_mutex);
395
396         t.wd = wd;
397
398         noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
399         if (noti_list) {
400                 del++;
401
402                 n = noti_list->data;
403                 g_notilist = g_list_remove(g_notilist, n);
404                 g_free(n->keyname);
405                 g_free(n);
406
407                 r = inotify_rm_watch(_kdb_inoti_fd, wd);
408                 if (r == -1) {
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;
412                 }
413
414                 INFO("key(%s) cb is removed. remained noti list total length(%d)",
415                                 keyname, g_list_length(g_notilist));
416         }
417
418         if (g_list_length(g_notilist) == 0) {
419                 close(_kdb_inoti_fd);
420                 _kdb_inoti_fd = 0;
421
422                 g_source_destroy(_kdb_handler);
423                 _kdb_handler = NULL;
424
425                 g_list_free(g_notilist);
426                 g_notilist = NULL;
427
428                 INFO("all noti list is freed");
429         }
430
431         pthread_mutex_unlock(&_kdb_g_ns_mutex);
432
433         if (del == 0) {
434                 errno = ENOENT;
435                 func_ret = PREFERENCE_ERROR_IO_ERROR;
436         }
437
438         return func_ret;
439 }
440