Added information config with inotify
[platform/core/connectivity/stc-manager.git] / src / helper / helper-inotify.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
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 <glib.h>
18
19 #include "helper-inotify.h"
20 #include "stc-manager-util.h"
21
22 typedef struct {
23         GIOChannel *channel;
24         uint watch;
25         int wd;
26
27         inotify_event_cb cb;
28 } stc_inotify_s;
29
30 static GHashTable *g_inotify_hash;
31
32 static gboolean __inotify_data(GIOChannel *channel, GIOCondition cond,
33                                                         gpointer user_data)
34 {
35         stc_inotify_s *inotify = user_data;
36         char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
37         char *next_event;
38         gsize bytes_read;
39         GIOStatus status;
40
41         if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
42                 inotify->watch = 0;
43                 return FALSE;
44         }
45
46         status = g_io_channel_read_chars(channel, buffer,
47                                         sizeof(buffer), &bytes_read, NULL);
48
49         switch (status) {
50         case G_IO_STATUS_NORMAL:
51                 break;
52         case G_IO_STATUS_AGAIN:
53                 return TRUE;
54         default:
55                 STC_LOGE("Reading from inotify channel failed");
56                 inotify->watch = 0;
57                 return FALSE;
58         }
59
60         next_event = buffer;
61
62         while (bytes_read > 0) {
63                 struct inotify_event *event;
64                 gchar *ident;
65                 gsize len;
66                 inotify_event_cb callback = inotify->cb;
67
68                 event = (struct inotify_event *) next_event;
69                 if (event->len)
70                         ident = next_event + sizeof(struct inotify_event);
71                 else
72                         ident = NULL;
73
74                 len = sizeof(struct inotify_event) + event->len;
75                 if (len > bytes_read)
76                         break;
77
78                 next_event += len;
79                 bytes_read -= len;
80
81                 (*callback)(event, ident);
82         }
83
84         return TRUE;
85 }
86
87 static void __remove_watch(stc_inotify_s *inotify)
88 {
89         int fd;
90
91         if (!inotify->channel)
92                 return;
93
94         if (inotify->watch > 0)
95                 g_source_remove(inotify->watch);
96
97         fd = g_io_channel_unix_get_fd(inotify->channel);
98
99         if (inotify->wd >= 0)
100                 inotify_rm_watch(fd, inotify->wd);
101
102         g_io_channel_unref(inotify->channel);
103 }
104
105 static int __create_watch(const char *path, stc_inotify_s *inotify)
106 {
107         int fd;
108
109         STC_LOGD("Add directory watch for [%s]", path);
110
111         fd = inotify_init();
112         if (fd < 0)
113                 return -EIO;
114
115         inotify->wd = inotify_add_watch(fd, path,
116                                         IN_MODIFY | IN_CREATE | IN_DELETE |
117                                         IN_MOVED_TO | IN_MOVED_FROM);
118         if (inotify->wd < 0) {
119                 STC_LOGE("Creation of [%s] watch failed", path);
120                 close(fd);
121                 return -EIO;
122         }
123
124         inotify->channel = g_io_channel_unix_new(fd);
125         if (!inotify->channel) {
126                 STC_LOGE("Creation of inotify channel failed");
127                 inotify_rm_watch(fd, inotify->wd);
128                 inotify->wd = 0;
129
130                 close(fd);
131                 return -EIO;
132         }
133
134         g_io_channel_set_close_on_unref(inotify->channel, TRUE);
135         g_io_channel_set_encoding(inotify->channel, NULL, NULL);
136         g_io_channel_set_buffered(inotify->channel, FALSE);
137
138         inotify->watch = g_io_add_watch(inotify->channel,
139                                 G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
140                                 __inotify_data, inotify);
141
142         return 0;
143 }
144
145 static void __inotify_destroy(gpointer user_data)
146 {
147         stc_inotify_s *inotify = user_data;
148
149         __remove_watch(inotify);
150         FREE(inotify);
151 }
152
153 int inotify_register(const char *path, inotify_event_cb callback)
154 {
155         int err;
156         stc_inotify_s *inotify;
157
158         if (!callback)
159                 return -EINVAL;
160
161         inotify = g_hash_table_lookup(g_inotify_hash, path);
162         if (inotify)
163                 goto update;
164
165         inotify = g_try_new0(stc_inotify_s, 1);
166         if (!inotify)
167                 return -ENOMEM;
168
169         inotify->wd = -1;
170
171         err = __create_watch(path, inotify);
172         if (err < 0) {
173                 FREE(inotify);
174                 return err;
175         }
176
177         g_hash_table_replace(g_inotify_hash, g_strdup(path), inotify);
178
179 update:
180         inotify->cb = callback;
181
182         return 0;
183 }
184
185 void inotify_deregister(const char *path)
186 {
187         stc_inotify_s *inotify;
188
189         inotify = g_hash_table_lookup(g_inotify_hash, path);
190         if (!inotify)
191                 return;
192
193         g_hash_table_remove(g_inotify_hash, path);
194 }
195
196 int inotify_initialize(void)
197 {
198         g_inotify_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
199                                                 g_free, __inotify_destroy);
200         return 0;
201 }
202
203 void inotify_deinitialize(void)
204 {
205         g_hash_table_destroy(g_inotify_hash);
206 }