22405b9c174d4acbabf4995ed85ad14da7ece25e
[platform/core/connectivity/stc-manager.git] / src / monitor / stc-monitor-app.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 "stc-db.h"
18 #include "counter.h"
19 #include "stc-manager.h"
20 #include "stc-monitor.h"
21 #include "stc-monitor-app.h"
22 #include "stc-monitor-rstn.h"
23 #include "stc-monitor-proc.h"
24 #include "stc-monitor-ipt.h"
25 #include "table-statistics.h"
26 #include "helper-net-cls.h"
27 #include "stc-manager-plugin-tether.h"
28
29 static void __print_app(gpointer key, gpointer value,
30                                           gpointer data)
31 {
32         stc_app_value_s *app_value = (stc_app_value_s *)value;
33
34         STC_LOGD("PkgID[%s] AppID[\033[0;32m%s\033[0;m] "
35                 "type[%d] classid[\033[1;36m%d\033[0;m] "
36                 "counter[in(%lld) out(%lld)]",
37                  app_value->pkg_id, app_value->app_id,
38                  app_value->type, app_value->classid,
39                  app_value->data_usage.in_bytes,
40                  app_value->data_usage.out_bytes);
41 }
42
43 static void __app_update_statistics(gpointer key,
44                                         gpointer value, gpointer data)
45 {
46         stc_app_value_s *app_value = (stc_app_value_s *)value;
47         time_t *touch_time = (time_t *)data;
48         stc_db_classid_iftype_key stat_key;
49         stc_db_app_stats stat;
50         default_connection_s *default_connection = stc_get_default_connection();
51
52         memset(&stat_key, 0, sizeof(stc_db_classid_iftype_key));
53         memset(&stat, 0 , sizeof(stc_db_app_stats));
54
55         /* Do not update statistics for Tethering
56          * if tethering is in-active found */
57         if (default_connection &&
58                 default_connection->tether_state == FALSE &&
59                 !strcmp(app_value->app_id, STC_TOTAL_TETHERING))
60                 return;
61
62         /* Do not update statistics for Wi-Fi
63          * if tethering is active on wlan0 iface */
64         if (default_connection && default_connection->tether_state &&
65                 default_connection->tether_iface.type == STC_IFACE_WIFI &&
66                 !strcmp(app_value->app_id, STC_TOTAL_WIFI))
67                 return;
68
69         stat_key.classid = app_value->classid;
70
71         if (app_value->classid == STC_TETHERING_APP_CLASSID &&
72                 default_connection->tether_state == TRUE)
73                 stat_key.iftype = default_connection->tether_iface.type;
74         else if (g_str_has_suffix(app_value->app_id, STC_TETHERING_APP_SUFFIX))
75                 stat_key.iftype = default_connection->tether_iface.type;
76         else
77                 stat_key.iftype = default_connection->type;
78
79         if (STC_IFACE_DATACALL == stat_key.iftype)
80                 stat_key.subscriber_id = g_strdup(default_connection->subscriber_id);
81         else
82                 stat_key.subscriber_id = g_strdup(SUBSCRIBERID_NONE);
83
84         if (app_value->classid == STC_TETHERING_APP_CLASSID &&
85                 default_connection->tether_state == TRUE)
86                 g_strlcpy(stat_key.ifname, default_connection->tether_iface.ifname,
87                           MAX_IFACE_LENGTH);
88         else if (g_str_has_suffix(app_value->app_id, STC_TETHERING_APP_SUFFIX))
89                 g_strlcpy(stat_key.ifname, default_connection->tether_iface.ifname,
90                           MAX_IFACE_LENGTH);
91         else
92                 g_strlcpy(stat_key.ifname, default_connection->ifname,
93                           MAX_IFACE_LENGTH);
94
95         stat.app_id = g_strdup(app_value->app_id);
96         stat.snd_count = app_value->counter.out_bytes;
97         stat.rcv_count = app_value->counter.in_bytes;
98         stat.is_roaming = default_connection->roaming;
99
100         if (strstr(stat.app_id, "_BACKGROUND")) {
101                 stat.ground = STC_APP_STATE_BACKGROUND;
102         } else {
103                 if (strstr(stat.app_id, "TOTAL_"))
104                         stat.ground = STC_APP_STATE_UNKNOWN;
105                 else
106                         stat.ground = STC_APP_STATE_FOREGROUND;
107         }
108
109         table_statistics_insert(&stat_key, &stat, *touch_time);
110
111         app_value->counter.out_bytes = 0;
112         app_value->counter.in_bytes = 0;
113
114         FREE(stat.app_id);
115         FREE(stat_key.subscriber_id);
116
117         return;
118 }
119
120 static gboolean __add_app_monitor_for_tethering(gpointer key,
121                                     gpointer value, gpointer data)
122 {
123         stc_app_value_s *app_value = (stc_app_value_s *)value;
124         default_connection_s *connection = (default_connection_s *)data;
125         stc_s *stc = stc_get_manager();
126         struct nfacct_rule counter;
127         char *ipaddr = NULL;
128         int ret;
129
130         STC_LOGI("Add appid(%s) classid(%d)", app_value->app_id,
131                         app_value->classid);
132
133         if (stc == NULL || connection == NULL)
134                 return FALSE;
135
136         if (!stc->carg) {
137                 stc->carg = MALLOC0(counter_arg_s, 1);
138                 if (stc->carg == NULL)
139                         return FALSE;
140
141                 stc->carg->sock = stc_monitor_get_contr_sock();
142         }
143
144         memset(&counter, 0, sizeof(struct nfacct_rule));
145
146         counter.carg = stc->carg;
147         counter.classid = app_value->classid;
148         counter.intend = NFACCT_TETH_COUNTER;
149
150         if (connection->tether_state != TRUE ||
151                         connection->tether_iface.ifname == NULL)
152                 return FALSE;
153
154         counter.iftype = connection->tether_iface.type;
155         g_strlcpy(counter.ifname, connection->tether_iface.ifname, MAX_IFACE_LENGTH);
156
157         /* get the ip address of the station based on its mac address */
158         ret = stc_plugin_tether_get_station_ip(app_value->mac, &ipaddr);
159         if (ret != STC_ERROR_NONE)
160                 return FALSE;
161
162         /* tethering iptables rule */
163         stc_monitor_tether_add_in(&counter, ipaddr);
164         stc_monitor_tether_add_out(&counter, ipaddr);
165
166         g_free(ipaddr);
167         return FALSE;
168 }
169
170 static gboolean __remove_app_monitor_for_tethering(gpointer key,
171                                 gpointer value, gpointer data)
172 {
173         stc_app_value_s *app_value = (stc_app_value_s *)value;
174         default_connection_s *connection = (default_connection_s *)data;
175         stc_s *stc = stc_get_manager();
176         struct nfacct_rule counter;
177         char *ipaddr = NULL;
178         int ret;
179
180         STC_LOGI("Remove appid(%s) classid(%d)", app_value->app_id,
181                         app_value->classid);
182
183         if (stc == NULL || connection == NULL)
184                 return FALSE;
185
186         if (!stc->carg) {
187                 stc->carg = MALLOC0(counter_arg_s, 1);
188                 if (stc->carg == NULL)
189                         return FALSE;
190
191                 stc->carg->sock = stc_monitor_get_contr_sock();
192         }
193
194         memset(&counter, 0, sizeof(struct nfacct_rule));
195
196         counter.carg = stc->carg;
197         counter.classid = app_value->classid;
198         counter.intend = NFACCT_TETH_COUNTER;
199
200         if (connection->tether_state != TRUE ||
201                         connection->tether_iface.ifname == NULL)
202                 return FALSE;
203
204         counter.iftype = connection->tether_iface.type;
205         g_strlcpy(counter.ifname, connection->tether_iface.ifname, MAX_IFACE_LENGTH);
206
207         /* get the ip address of the station based on its mac address */
208         ret = stc_plugin_tether_get_station_ip(app_value->mac, &ipaddr);
209         if (ret != STC_ERROR_NONE)
210                 return FALSE;
211
212         stc_monitor_tether_del_in(&counter, ipaddr);
213         stc_monitor_tether_del_out(&counter, ipaddr);
214
215         g_free(ipaddr);
216         return FALSE;
217 }
218
219 static void __app_value_destroy(gpointer data)
220 {
221         stc_app_value_s *app_value = (stc_app_value_s *)data;
222
223         FREE(app_value->pkg_id);
224         FREE(app_value->app_id);
225         g_hash_table_destroy(app_value->processes);
226         app_value->processes = NULL;
227
228         FREE(app_value);
229 }
230
231 static void __check_rstn_limit_exceeded(gpointer data,
232                                 gpointer user_data)
233 {
234         stc_rstn_data_s *rstn_data = (stc_rstn_data_s *)data;
235         int32_t *limit_exceeded = (int32_t *)user_data;
236
237         if (rstn_data->limit_exceeded != 0)
238                 *limit_exceeded = rstn_data->limit_exceeded;
239 }
240
241 static void __app_update_counter(classid_bytes_context_s *context,
242                                 uint32_t classid)
243 {
244         stc_app_value_s *lookup_app;
245         stc_rstn_value_s *lookup_rstn;
246         GHashTable *apps = stc_monitor_get_system_apps();
247         GHashTable *rstns = stc_monitor_get_system_rstns();
248
249         if (!rstns)
250                 return;
251
252         lookup_rstn = g_hash_table_lookup(rstns, GUINT_TO_POINTER(classid));
253         if (lookup_rstn) {
254                 int32_t limit_exceeded = 0;
255                 g_slist_foreach(lookup_rstn->rules,
256                         __check_rstn_limit_exceeded, &limit_exceeded);
257
258                 if (limit_exceeded != 0)
259                         return;
260         }
261
262         if (!apps)
263                 return;
264
265         lookup_app = g_hash_table_lookup(apps, GUINT_TO_POINTER(classid));
266         if (lookup_app)
267                 stc_monitor_app_update_counter(lookup_app, context);
268 }
269
270 void stc_monitor_app_update_counter(stc_app_value_s *value,
271                                  classid_bytes_context_s *context)
272 {
273         switch (context->counter->iotype) {
274         case NFACCT_COUNTER_IN:
275                 value->data_usage.in_bytes += context->bytes;
276                 value->counter.in_bytes = context->bytes;
277                 stc_monitor_set_apps_updated(TRUE);
278
279                 break;
280         case NFACCT_COUNTER_OUT:
281                 value->data_usage.out_bytes += context->bytes;
282                 value->counter.out_bytes = context->bytes;
283                 stc_monitor_set_apps_updated(TRUE);
284
285                 break;
286         default:
287                 STC_LOGE("Unknown iotype");
288         }
289 }
290
291 void stc_monitor_app_update_iface_counter(classid_bytes_context_s *context)
292 {
293         switch (context->counter->iftype) {
294         case STC_IFACE_DATACALL:
295                 __app_update_counter(context, STC_TOTAL_DATACALL_CLASSID);
296                 break;
297         case STC_IFACE_WIFI:
298                 __app_update_counter(context, STC_TOTAL_WIFI_CLASSID);
299                 __app_update_counter(context, STC_TETHERING_APP_CLASSID);
300                 break;
301         case STC_IFACE_BLUETOOTH:
302                 __app_update_counter(context, STC_TOTAL_BLUETOOTH_CLASSID);
303                 __app_update_counter(context, STC_TETHERING_APP_CLASSID);
304                 break;
305         case STC_IFACE_USB:
306                 __app_update_counter(context, STC_TETHERING_APP_CLASSID);
307                 break;
308         case STC_IFACE_P2P:
309                 __app_update_counter(context, STC_TETHERING_APP_CLASSID);
310                 break;
311         default:
312                 break;
313         }
314 }
315
316 gboolean stc_monitor_app_flush_stats_to_db(gpointer user_data)
317 {
318         time_t current_time = 0;
319         stc_s *stc = stc_get_manager();
320         GHashTable *apps = stc_monitor_get_system_apps();
321         gboolean apps_updated = stc_monitor_get_apps_updated();
322
323         if (stc && stc->carg)
324                 current_time = stc->carg->last_run_time;
325
326         if (apps_updated == FALSE)
327                 return G_SOURCE_REMOVE;
328
329         stc_monitor_set_apps_updated(FALSE);
330
331         if (apps)
332                 g_hash_table_foreach(apps,
333                                         __app_update_statistics,
334                                         &current_time);
335
336         STC_LOGI("Flushed app stats to database");
337         return G_SOURCE_REMOVE;
338 }
339
340 API stc_error_e stc_monitor_app_add(uint32_t classid,
341                                 const char *app_id,
342                                 const char *pkg_id,
343                                 const stc_app_value_s value)
344 {
345         stc_app_value_s *app_value;
346         stc_app_value_s *lookup_value;
347
348         GHashTable *apps = stc_monitor_get_system_apps();
349
350         if (!apps)
351                 return STC_ERROR_NO_DATA;
352
353         if (classid == STC_UNKNOWN_CLASSID)
354                 classid = get_classid_by_app_id(app_id, TRUE);
355
356         lookup_value = g_hash_table_lookup(apps, GUINT_TO_POINTER(classid));
357         if (lookup_value) {
358                 if (STC_DEBUG_LOG)
359                         STC_LOGE("Already exists [\033[1;36m%d\033[0;m:"
360                                                 "\033[0;32m%s\033[0;m]", classid, app_id);
361                 return STC_ERROR_NONE;
362         }
363
364         app_value = MALLOC0(stc_app_value_s, 1);
365         if (!app_value) {
366                 if (STC_DEBUG_LOG)
367                         STC_LOGE("Value allocation failed");
368                 return STC_ERROR_OUT_OF_MEMORY;
369         }
370
371         /* create cgroup and update classid */
372         app_value->classid = classid;
373
374         app_value->app_id = g_strdup(app_id);
375         app_value->pkg_id = g_strdup(pkg_id);
376
377         app_value->type = value.type;
378         app_value->data_usage.in_bytes = value.data_usage.in_bytes;
379         app_value->data_usage.out_bytes = value.data_usage.out_bytes;
380         g_strlcpy(app_value->mac, value.mac, MAC_ADDRESS_LEN);
381
382         app_value->processes = g_hash_table_new_full(g_direct_hash,
383                                                                 g_direct_equal, NULL, NULL);
384
385         /* update classid for tethering station based on its mac address */
386         if (g_str_has_suffix(app_id, STC_TETHERING_APP_SUFFIX) &&
387                                 classid != STC_TETHERING_APP_CLASSID)
388                 stc_plugin_tether_set_station_classid(app_value->mac, classid);
389
390         g_hash_table_insert(apps, GUINT_TO_POINTER(classid), app_value);
391
392         /* add nfacct rule for this classid */
393         stc_monitor_app_add_monitor(GUINT_TO_POINTER(classid),
394                                 app_value, stc_get_default_connection());
395         stc_monitor_rstn_add_for_app(classid);
396
397         if (STC_DEBUG_LOG) {
398                 __print_app(GUINT_TO_POINTER(classid), app_value, NULL);
399                 STC_LOGD("\033[1;32mApplication added\033[0;m "
400                         "[\033[1;36m%d\033[0;m]", classid);
401         }
402
403         return STC_ERROR_NONE;
404 }
405
406 void stc_monitor_app_add_by_iface(const char *ifname)
407 {
408         stc_app_value_s app_value;
409
410         if (ifname == NULL)
411                 return;
412
413         memset(&app_value, 0, sizeof(stc_app_value_s));
414
415         app_value.type = STC_APP_TYPE_NONE;
416         app_value.processes = NULL;
417         app_value.counter.in_bytes = 0;
418         app_value.counter.out_bytes = 0;
419
420         stc_monitor_app_add(STC_UNKNOWN_CLASSID, ifname, ifname, app_value);
421 }
422
423 void stc_monitor_app_add_monitor(gpointer key,
424                                 gpointer value, gpointer data)
425 {
426         stc_app_value_s *app_value = (stc_app_value_s *)value;
427         default_connection_s *connection = (default_connection_s *)data;
428         stc_s *stc = stc_get_manager();
429
430         if (app_value->classid == STC_TOTAL_DATACALL_CLASSID ||
431             app_value->classid == STC_TOTAL_WIFI_CLASSID ||
432             app_value->classid == STC_TOTAL_BLUETOOTH_CLASSID)
433                 return;
434
435         if (stc && connection && connection->ifname) {
436                 struct nfacct_rule counter;
437
438                 if (!stc->carg) {
439                         stc->carg = MALLOC0(counter_arg_s, 1);
440                         if (stc->carg == NULL)
441                                 return;
442
443                         stc->carg->sock = stc_monitor_get_contr_sock();
444                 }
445
446                 memset(&counter, 0, sizeof(struct nfacct_rule));
447
448                 counter.carg = stc->carg;
449                 counter.classid = app_value->classid;
450                 counter.intend = NFACCT_COUNTER;
451
452                 if (connection->tether_state == TRUE &&
453                         connection->tether_iface.ifname != NULL &&
454                         app_value->classid == STC_TETHERING_APP_CLASSID) {
455                         counter.iftype = connection->tether_iface.type;
456                         g_strlcpy(counter.ifname, connection->tether_iface.ifname, MAX_IFACE_LENGTH);
457                 } else {
458                         counter.iftype = connection->type;
459                         g_strlcpy(counter.ifname, connection->ifname, MAX_IFACE_LENGTH);
460                 }
461
462                 if (g_str_has_suffix(app_value->app_id, STC_TETHERING_APP_SUFFIX) &&
463                                 app_value->classid != STC_TETHERING_APP_CLASSID) {
464                         __add_app_monitor_for_tethering(key, value, data);
465                 } else if (app_value->classid == STC_TOTAL_IPV4_CLASSID) {
466                         stc_monitor_ipt_add_in(&counter);
467                         stc_monitor_ipt_add_out(&counter);
468                 } else if (app_value->classid == STC_TOTAL_IPV6_CLASSID) {
469                         stc_monitor_ip6t_add_in(&counter);
470                         stc_monitor_ip6t_add_out(&counter);
471                 } else {
472                         stc_monitor_ipt_add_in(&counter);
473                         stc_monitor_ipt_add_out(&counter);
474                         stc_monitor_ip6t_add_in(&counter);
475                         stc_monitor_ip6t_add_out(&counter);
476                 }
477         }
478 }
479
480 void stc_monitor_app_add_by_connection(default_connection_s *conn)
481 {
482         GHashTable *apps = stc_monitor_get_system_apps();
483
484         if (!apps)
485                 return;
486
487         g_hash_table_foreach(apps, stc_monitor_app_add_monitor, conn);
488 }
489
490 API stc_error_e stc_monitor_app_remove(uint32_t classid, const char *app_id)
491 {
492         stc_app_value_s *app_lookup;
493         GHashTable *apps = stc_monitor_get_system_apps();
494
495         if (!apps)
496                 return STC_ERROR_NO_DATA;
497
498         classid = get_classid_by_app_id(app_id, FALSE);
499
500         app_lookup = g_hash_table_lookup(apps, GUINT_TO_POINTER(classid));
501         if (!app_lookup) {
502                 if (STC_DEBUG_LOG)
503                         STC_LOGD("Application not found [\033[1;36m%d\033[0;m]", classid);
504                 return STC_ERROR_FAIL;
505         }
506
507         /* remove nfacct rule for this classid */
508         stc_monitor_app_remove_monitor(GUINT_TO_POINTER(classid),
509                                 app_lookup, stc_get_default_connection());
510
511         /* remove ristrictions if any */
512         stc_monitor_rstn_remove_for_app(classid);
513
514         if (STC_DEBUG_LOG)
515                 __print_app(GUINT_TO_POINTER(classid), app_lookup, NULL);
516
517         /* remove app_key from the stc-manager */
518         g_hash_table_remove(apps, GUINT_TO_POINTER(classid));
519
520         return STC_ERROR_NONE;
521 }
522
523 void stc_monitor_app_remove_monitor(gpointer key,
524                                 gpointer value, gpointer data)
525 {
526         stc_app_value_s *app_value = (stc_app_value_s *)value;
527         default_connection_s *connection = (default_connection_s *)data;
528         stc_s *stc = stc_get_manager();
529
530         if (stc && connection && connection->ifname) {
531                 struct nfacct_rule counter;
532
533                 if (!stc->carg) {
534                         stc->carg = MALLOC0(counter_arg_s, 1);
535                         if (stc->carg == NULL)
536                                 return;
537
538                         stc->carg->sock = stc_monitor_get_contr_sock();
539                 }
540
541                 memset(&counter, 0, sizeof(struct nfacct_rule));
542
543                 counter.carg = stc->carg;
544                 counter.classid = app_value->classid;
545                 counter.intend = NFACCT_COUNTER;
546
547                 if (g_str_has_suffix(app_value->app_id, STC_TETHERING_APP_SUFFIX) &&
548                                 app_value->classid != STC_TETHERING_APP_CLASSID) {
549                         __remove_app_monitor_for_tethering(key, value, data);
550                         return;
551                 } else if (connection->tether_state == FALSE &&
552                         connection->tether_iface.ifname != NULL &&
553                         app_value->classid == STC_TETHERING_APP_CLASSID) {
554                         counter.iftype = connection->tether_iface.type;
555                         g_strlcpy(counter.ifname, connection->tether_iface.ifname, MAX_IFACE_LENGTH);
556                 } else {
557                         counter.iftype = connection->type;
558                         g_strlcpy(counter.ifname, connection->ifname, MAX_IFACE_LENGTH);
559                 }
560
561                 stc_monitor_ipt_del_in(&counter);
562                 stc_monitor_ipt_del_out(&counter);
563                 stc_monitor_ip6t_del_in(&counter);
564                 stc_monitor_ip6t_del_out(&counter);
565         }
566
567         return;
568 }
569
570 void stc_monitor_app_remove_by_connection(default_connection_s *conn)
571 {
572         GHashTable *apps = stc_monitor_get_system_apps();
573
574         if (!apps)
575                 return;
576
577         g_hash_table_foreach(apps, stc_monitor_app_remove_monitor, conn);
578 }
579
580 GHashTable *stc_monitor_apps_init(void)
581 {
582         return g_hash_table_new_full(g_direct_hash, g_direct_equal,
583                                         NULL, __app_value_destroy);
584 }