Fix a memory leak
[platform/core/connectivity/stc-manager.git] / plugin / tether / stc-plugin-tether.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 <vconf/vconf.h>
18 #include <stdbool.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <glib.h>
25
26 #include "stc-monitor.h"
27 #include "stc-plugin-tether.h"
28
29 static GSList *station_list = NULL;
30 static GDBusConnection *connection = NULL;
31 static GCancellable *cancellable = NULL;
32 static int g_mobileap_signal_sub_id = 0;
33
34 static stc_error_e add_station_monitor(gchar *pkg_id, gchar *app_id,
35                          const char *mac)
36 {
37         int ret;
38         stc_app_key_s app_key;
39         stc_app_value_s app_value;
40
41         if (pkg_id == NULL || app_id == NULL || mac == NULL) {
42                 STC_LOGE("invalid station station info");
43                 return STC_ERROR_INVALID_PARAMETER;
44         }
45
46         memset(&app_key, 0, sizeof(stc_app_key_s));
47         memset(&app_value, 0, sizeof(stc_app_value_s));
48         app_key.pkg_id = g_strdup(pkg_id);
49         app_key.app_id = g_strconcat(app_id, STC_TETHERING_APP_SUFFIX, NULL);
50         app_value.type = STC_APP_TYPE_TETHERING;
51         app_value.processes = NULL;
52         g_strlcpy(app_value.mac, mac, STATION_MAC_STR_LEN);
53
54         ret = stc_monitor_application_add(app_key, app_value);
55         FREE(app_key.pkg_id);
56         FREE(app_key.app_id);
57         return ret;
58 }
59
60 static stc_error_e remove_station_monitor(gchar *pkg_id, gchar *app_id)
61 {
62         int ret;
63         stc_app_key_s app_key;
64
65         if (pkg_id == NULL || app_id == NULL) {
66                 STC_LOGE("invalid station station info");
67                 return STC_ERROR_INVALID_PARAMETER;
68         }
69
70         memset(&app_key, 0, sizeof(stc_app_key_s));
71         app_key.pkg_id = g_strdup(pkg_id);
72         app_key.app_id = g_strconcat(app_id, STC_TETHERING_APP_SUFFIX, NULL);
73
74         ret = stc_monitor_application_remove(app_key);
75         FREE(app_key.pkg_id);
76         FREE(app_key.app_id);
77         return ret;
78 }
79
80 static int _compare_sta_by_mac_func(gconstpointer a,
81                 gconstpointer b)
82 {
83         tether_sta_info_s *si = (tether_sta_info_s *)a;
84         return g_ascii_strcasecmp(si->mac, (const char *)b);
85 }
86
87 static int _compare_sta_by_classid_func(gconstpointer a,
88                 gconstpointer b)
89 {
90         tether_sta_info_s *si = (tether_sta_info_s *)a;
91         int *classid = (int *)b;
92         return si->classid - *classid;
93 }
94
95 static int _get_station_info(gconstpointer data, GCompareFunc func,
96                 tether_sta_info_s **si)
97 {
98         GSList *list = station_list;
99         tether_sta_info_s *info = NULL;
100
101         if (func == NULL || si == NULL)
102                 return -1;
103
104         if (!list)
105                 return -1;
106
107         list = g_slist_find_custom(list, data, func);
108         if (list == NULL)
109                 return -1;
110
111         info = list->data;
112         *si = info;
113         return 0;
114 }
115
116 static void _remove_station_info(gconstpointer data, GCompareFunc func)
117 {
118         GSList *list = station_list;
119         tether_sta_info_s *info = NULL;
120         if (func == NULL)
121                 return;
122
123         if (!list)
124                 return;
125
126         list = g_slist_find_custom(list, data, func);
127         if (list == NULL)
128                 return;
129
130         info = (tether_sta_info_s *)list->data;
131         STC_LOGI("STA-REMOVED: (%s) (%s) (%s)", info->ip, info->mac, info->name);
132
133         /* remove tethering client from monitoring */
134         remove_station_monitor(info->mac, info->station_id);
135
136         g_free(info->station_id);
137         g_free(info);
138
139         station_list = g_slist_delete_link(station_list, list);
140 }
141
142 static void _add_station_info(tether_sta_info_s *info)
143 {
144         tether_sta_info_s *tmp = NULL;
145         if (info == NULL) {
146                 STC_LOGE("info is NULL");
147                 return;
148         }
149
150         if (_get_station_info(info->mac, _compare_sta_by_mac_func, &tmp) == 0) {
151                 if (!g_strcmp0(tmp->name, info->name) && !g_strcmp0(tmp->ip, info->ip))
152                         return;
153
154                 /* Remove the station if dhcp info changed. */
155                 _remove_station_info(info->mac, _compare_sta_by_mac_func);
156         }
157
158         station_list = g_slist_prepend(station_list, info);
159         STC_LOGI("STA-ADDED: (%s) (%s) (%s)", info->ip, info->mac, info->name);
160
161         /* add tethering client for monitoring data usage */
162         info->station_id = g_strdup_printf("%s_%s", info->mac, info->name);
163         add_station_monitor(info->mac, info->station_id, info->mac);
164 }
165
166 static void _mobileap_signal_cb(GDBusConnection *conn,
167                                const gchar *name, const gchar *path,
168                                const gchar *interface, const gchar *sig,
169                                GVariant *param, gpointer user_data)
170 {
171         int type;
172         int tm;
173         char *ip = NULL;
174         char *mac = NULL;
175         char *hostname = NULL;
176         char *state = NULL;
177         tether_sta_info_s *sta = NULL;
178
179         ret_msg_if(sig == NULL, "signal name NULL");
180         ret_msg_if(param == NULL, "param NULL");
181
182         STC_LOGI("%s interface(%s)", sig, interface);
183
184         g_variant_get(param, "(susssu)", &state, &type, &ip, &mac, &hostname, &tm);
185         STC_LOGI("%s: ip(%s) mac(%s) name(%s) tm(%d)", state, ip, mac, hostname, tm);
186
187         if (!g_strcmp0(state, "DhcpConnected")) {
188                 sta = (tether_sta_info_s *)g_malloc0(sizeof(tether_sta_info_s));
189                 if (sta == NULL) {
190                         STC_LOGE("g_malloc0 failed");
191                         return;
192                 }
193
194                 g_strlcpy(sta->ip, ip, INET_ADDRSTRLEN);
195                 g_strlcpy(sta->mac, mac, STATION_MAC_STR_LEN);
196                 g_strlcpy(sta->name, hostname, STATION_STR_HOSTNAME_LEN);
197                 _add_station_info(sta);
198         } else if (!g_strcmp0(state, "DhcpLeaseDeleted")) {
199                 _remove_station_info(mac, _compare_sta_by_mac_func);
200         }
201
202         g_free(state);
203         g_free(ip);
204         g_free(mac);
205         g_free(hostname);
206 }
207
208 stc_error_e tether_plugin_get_station_ip(const char *mac, char *ip)
209 {
210         tether_sta_info_s *tmp = NULL;
211
212         if (mac == NULL || ip == NULL)
213                 return STC_ERROR_FAIL;
214
215         if (_get_station_info((gconstpointer)mac,
216                                 _compare_sta_by_mac_func, &tmp) != 0) {
217                 STC_LOGE("mac(%s) not found", mac);
218                 return STC_ERROR_FAIL;
219         }
220
221         g_strlcpy(ip, tmp->ip, INET_ADDRSTRLEN);
222         return STC_ERROR_NONE;
223 }
224
225 stc_error_e tether_plugin_get_station_by_classid(const int classid, char *mac)
226 {
227         tether_sta_info_s *tmp = NULL;
228         int classid_value = classid;
229
230         if (mac == NULL)
231                 return STC_ERROR_FAIL;
232
233         if (_get_station_info((gconstpointer)&classid_value,
234                                 _compare_sta_by_classid_func, &tmp) != 0) {
235                 STC_LOGE("classid(%s) not found", classid);
236                 return STC_ERROR_FAIL;
237         }
238
239         g_strlcpy(mac, tmp->mac, STATION_MAC_STR_LEN);
240         return STC_ERROR_NONE;
241 }
242
243 stc_error_e tether_plugin_set_station_classid(const char *mac, int classid)
244 {
245         tether_sta_info_s *tmp = NULL;
246
247         if (mac == NULL) {
248                 STC_LOGE("invalid param");
249                 return STC_ERROR_FAIL;
250         }
251
252         if (_get_station_info((gconstpointer)mac,
253                                 _compare_sta_by_mac_func, &tmp) != 0) {
254                 STC_LOGE("mac(%s) not found", mac);
255                 return STC_ERROR_FAIL;
256         }
257
258         if (tmp)
259                 tmp->classid = classid;
260
261         return STC_ERROR_NONE;
262 }
263
264 int tether_plugin_init(void)
265 {
266         GError *error = NULL;
267
268         if (connection)
269                 return 0;
270
271         connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
272         if (!connection) {
273                 g_error_free(error);
274                 return -1;
275         }
276
277         cancellable = g_cancellable_new();
278
279         g_mobileap_signal_sub_id = g_dbus_connection_signal_subscribe(connection,
280                                                    NULL,
281                                                    TETHERING_SERVICE_INTERFACE,
282                                                    SIGNAL_NAME_DHCP_STATUS,
283                                                    NULL, NULL,
284                                                    G_DBUS_SIGNAL_FLAGS_NONE,
285                                                    _mobileap_signal_cb,
286                                                    NULL, NULL);
287
288         STC_LOGI("tether plugin initialised");
289         return 0;
290 }
291
292 void tether_plugin_deinit(void)
293 {
294         if (!connection)
295                 return;
296
297         g_object_unref(connection);
298         g_object_unref(cancellable);
299         connection = NULL;
300         cancellable = NULL;
301         STC_LOGI("tether plugin deinitialised");
302 }
303
304 API stc_plugin_tether_s tether_plugin = {
305         .init = tether_plugin_init,
306         .deinit = tether_plugin_deinit,
307         .get_station_ip = tether_plugin_get_station_ip,
308         .get_station_by_classid = tether_plugin_get_station_by_classid,
309         .set_station_classid = tether_plugin_set_station_classid
310 };