a26749fd26bc73affda1712455e49498de11b3fb
[platform/core/connectivity/stc-manager.git] / plugin / monitor / stc-plugin-monitor.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 <linux/netlink.h>
18 #include <vconf.h>
19 #include <vconf-keys.h>
20
21 #include "helper-nl.h"
22 #include "helper-nfacct-rule.h"
23 #include "helper-net-cls.h"
24 #include "helper-cgroup.h"
25 #include "helper-iptables.h"
26 #include "counter.h"
27 #include "table-statistics.h"
28 #include "table-counters.h"
29 #include "stc-plugin-monitor.h"
30 #include "stc-plugin-monitor-connection.h"
31 #include "stc-plugin-monitor-rstn.h"
32 #include "stc-manager-plugin-exception.h"
33
34 static stc_system_s *g_system = NULL;
35 static stc_manager_stop_cb g_stop_cb = NULL;
36
37 static int __vconf_get_int(const char *key, int *value)
38 {
39         int ret = 0;
40
41         ret = vconf_get_int(key, value);
42         if (ret != VCONF_OK) {
43                 STC_LOGE("Failed to get vconfkey [%s] value", key);
44                 return -1;
45         }
46
47         return 0;
48 }
49
50 static stc_error_e __close_contr_sock(stc_system_s *system)
51 {
52         ret_value_msg_if(system == NULL, STC_ERROR_INVALID_PARAMETER, "invalid parameter");
53
54         /* close netlink socket for updating kernel counters */
55         if (system->contr_sock != -1) {
56                 close(system->contr_sock);
57                 system->contr_sock = -1;
58         }
59
60         if (system->contr_gsource_id != 0) {
61                 g_source_remove(system->contr_gsource_id);
62                 system->contr_gsource_id = 0;
63         }
64
65         return STC_ERROR_NONE;
66 }
67
68 static gboolean __process_contr_reply(GIOChannel *source,
69                                       GIOCondition condition,
70                                       gpointer user_data);
71
72 static void __free_system(stc_system_s *system)
73 {
74         /* destroy monitored application tree */
75         if (system->apps)
76                 g_hash_table_destroy(system->apps);
77         /* destroy restriction rules tree */
78         if (system->rstns)
79                 g_hash_table_destroy(system->rstns);
80         FREE(system);
81 }
82
83 static stc_error_e __close_and_reopen_contr_sock(stc_system_s *system)
84 {
85         GIOChannel *gio = NULL;
86         ret_value_msg_if(system == NULL, STC_ERROR_INVALID_PARAMETER, "invalid parameter");
87
88         /* close netlink socket for updating kernel counters */
89         if (system->contr_sock != -1) {
90                 close(system->contr_sock);
91                 system->contr_sock = -1;
92         }
93
94         if (system->contr_gsource_id != 0) {
95                 g_source_remove(system->contr_gsource_id);
96                 system->contr_gsource_id = 0;
97         }
98
99         /* create netlink socket for updating kernel counters */
100         system->contr_sock = create_netlink(NETLINK_NETFILTER, 0);
101         if (system->contr_sock < 0) {
102                 STC_LOGE("failed to open socket");
103                 __free_system(system);
104                 g_system = NULL;
105                 g_stop_cb();
106                 return STC_ERROR_FAIL;
107         }
108
109         gio = g_io_channel_unix_new(system->contr_sock);
110         system->contr_gsource_id =
111                 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
112                                (GIOFunc) __process_contr_reply,
113                                NULL);
114         g_io_channel_unref(gio);
115
116         return STC_ERROR_NONE;
117 }
118
119 static void __check_rstn_limit_exceeded(gpointer data,
120                                 gpointer user_data)
121 {
122         stc_rstn_data_s *rstn_data = (stc_rstn_data_s *)data;
123         int32_t *limit_exceeded = (int32_t *)user_data;
124
125         if (rstn_data->limit_exceeded != 0)
126                 *limit_exceeded = rstn_data->limit_exceeded;
127 }
128
129 static void __fill_nfacct_result(char *cnt_name, long long int bytes,
130                                 struct counter_arg *carg)
131 {
132         stc_monitor_rstn_reset_time_counters_if_required();
133
134         struct nfacct_rule counter = {
135                 .carg = carg,
136                 .name = {0},
137                 .ifname = {0},
138                 0
139         };
140
141         classid_bytes_context_s context = {
142                 .counter = &counter,
143                 .bytes = bytes,
144                 .data_limit_exceeded = FALSE,
145         };
146
147         if (!recreate_counter_by_name(cnt_name, &counter)) {
148                 STC_LOGE("Can't parse counter name %s", cnt_name);
149                 return;
150         }
151
152         if (STC_DEBUG_LOG)
153                 STC_LOGI("classid[\033[1;36m%u\033[0;m] iftype[%u] "
154                         "iotype[%d] intend[%d] ifname[%s] bytes[%lld]",
155                         context.counter->classid, context.counter->iftype,
156                         context.counter->iotype, context.counter->intend,
157                         context.counter->ifname, context.bytes);
158
159         if (context.counter->intend == NFACCT_COUNTER ||
160                 context.counter->intend == NFACCT_TETH_COUNTER) {
161                 if (g_system->apps) {
162                         stc_app_value_s *lookup_app;
163                         stc_rstn_value_s *lookup_rstn;
164
165                         stc_monitor_app_update_iface_counter(&context);
166
167                         lookup_app = g_hash_table_lookup(g_system->apps,
168                                                 GUINT_TO_POINTER(context.counter->classid));
169                         if (lookup_app)
170                                 stc_monitor_app_update_counter(lookup_app, &context);
171
172                         lookup_rstn = g_hash_table_lookup(g_system->rstns,
173                                                 GUINT_TO_POINTER(context.counter->classid));
174                         if (lookup_rstn) {
175                                 int32_t limit_exceeded = 0;
176                                 g_slist_foreach(lookup_rstn->rules,
177                                         __check_rstn_limit_exceeded, &limit_exceeded);
178
179                                 if (limit_exceeded != 0)
180                                         return;
181                         }
182                 }
183
184                 if (g_system->rstns) {
185                         stc_rstn_value_s *lookup_value;
186                         uint32_t classid = context.counter->classid;
187
188                         stc_monitor_rstn_update_iface_counter(&context);
189                         context.counter->classid = classid;
190
191                         lookup_value = g_hash_table_lookup(g_system->rstns,
192                                                         GUINT_TO_POINTER(classid));
193                         if (lookup_value) {
194                                 g_slist_foreach(lookup_value->rules,
195                                         stc_monitor_rstn_update_counter,
196                                         &context);
197                         }
198                 }
199         }
200 }
201
202 static int __fill_counters(struct rtattr *attr_list[__NFACCT_MAX],
203                            void *user_data)
204 {
205         struct counter_arg *carg = user_data;
206         char *cnt_name = (char *)RTA_DATA(attr_list[NFACCT_NAME]);
207         if (carg->initiate) {
208                 /**
209                  * TODO: this will be used when daemon starts to update existing
210                  * counter data if present.
211                  *
212                  populate_counters(cnt_name, carg);
213                  */
214         } else {
215                 long long int *bytes_p =
216                         (long long int *)RTA_DATA(attr_list[NFACCT_BYTES]);
217                 int bytes = be64toh(*bytes_p);
218                 if (bytes) {
219                         ++carg->serialized_counters;
220                         __fill_nfacct_result(cnt_name, bytes, carg);
221                 }
222         }
223
224         return 0;
225 }
226
227 static int __post_fill_counters(void *user_data)
228 {
229         struct counter_arg *carg = user_data;
230
231         if (carg->initiate)
232                 carg->initiate = 0;
233
234         return 0;
235 }
236
237 static void __process_network_counter(struct genl *ans,
238                                       struct counter_arg *carg)
239 {
240         struct netlink_serialization_params ser_params = {
241                 .carg = carg,
242                 .ans = ans,
243                 .eval_attr = __fill_counters,
244                 .post_eval_attr = __post_fill_counters,
245         };
246
247         netlink_serialization_command *netlink =
248                 netlink_create_command(&ser_params);
249         if (!netlink) {
250                 STC_LOGE("Can not create command");
251                 return;
252         }
253
254         netlink->deserialize_answer(&(netlink->params));
255 }
256
257 static gboolean __process_contr_reply(GIOChannel *source,
258                                       GIOCondition condition,
259                                       gpointer user_data)
260 {
261         int sock = g_io_channel_unix_get_fd(source);
262         struct genl *ans;
263         int ret;
264         stc_s *stc = stc_get_manager();
265
266 #ifdef TIZEN_GTESTS
267         void __gcov_flush(void);
268         __gcov_flush();
269 #endif
270
271         if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
272             (condition & G_IO_NVAL)) {
273                 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
274
275                 STC_LOGE("Counter socket received G_IO event, closing socket."
276                          "G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
277                          (condition & G_IO_ERR), (condition & G_IO_HUP),
278                          (condition & G_IO_NVAL));
279                 __close_and_reopen_contr_sock(g_system);
280                 return FALSE;
281         }
282
283         ans = MALLOC0(struct genl, 1);
284         if (ans == NULL) {
285                 STC_LOGE("Failed allocate memory to genl reply message");
286                 return TRUE;
287         }
288
289         if (stc == NULL) {
290                 STC_LOGE("Can't get stc data");
291                 goto out;
292         }
293
294         ret = read_netlink(sock, ans, sizeof(struct genl));
295
296         if (ret == 0)
297                 goto out;
298
299         stc->carg->ans_len = ret;
300         stc->carg->last_run_time = time(NULL);
301
302         __process_network_counter(ans, stc->carg);
303
304         g_idle_add(stc_monitor_app_flush_stats_to_db, NULL);
305         g_idle_add(stc_monitor_rstn_flush_contr_to_db, NULL);
306 out:
307
308         FREE(ans);
309         return TRUE;
310 }
311
312 static gboolean __update_contr_cb(void *user_data)
313 {
314         /* Here we just sent command, answer we receive in another callback */
315         stc_s *stc = stc_get_manager();
316         ret_value_msg_if(stc == NULL, STC_ERROR_FAIL, "Can't get stc data");
317         if (!stc->carg) {
318                 stc->carg = MALLOC0(counter_arg_s, 1);
319                 if (stc->carg == NULL)
320                         return TRUE;  /* we need to continue the timer */
321
322                 stc->carg->sock = g_system->contr_sock;
323         }
324
325 #ifdef TIZEN_GTESTS
326         void __gcov_flush(void);
327         __gcov_flush();
328 #endif
329
330         /* STC_LOGD("Get all counters"); */
331         nfacct_send_get_all(stc->carg);
332
333         /* we need to continue the timer */
334         return TRUE;
335 }
336
337 static void __fill_exceptions_list(void)
338 {
339         stc_plugin_fill_exception_list();
340 }
341
342 stc_error_e stc_plugin_monitor_initialize(stc_manager_stop_cb stop_cb)
343 {
344         __STC_LOG_FUNC_ENTER__;
345
346         stc_system_s *system = MALLOC0(stc_system_s, 1);
347         GIOChannel *gio = NULL;
348
349         ret_value_msg_if(system == NULL, STC_ERROR_OUT_OF_MEMORY,
350                                 "stc_system_s malloc fail!");
351
352         ret_value_msg_if(stop_cb == NULL, STC_ERROR_INVALID_PARAMETER,
353                                 "stop_cb callback is NULL");
354
355         g_stop_cb = stop_cb;
356
357         /* initializing current classid */
358         init_current_classid();
359
360         /* initializing cgroups */
361         cgroup_init();
362
363         /* creating monitored application tree */
364         system->apps = stc_monitor_apps_init();
365         system->rstns = stc_monitor_rstns_init();
366
367         /* create netlink socket for updating kernel counters */
368         system->contr_sock = create_netlink(NETLINK_NETFILTER, 0);
369         if (system->contr_sock < 0) {
370                 STC_LOGE("failed to open socket");
371                 __free_system(system);
372                 g_stop_cb();
373                 return STC_ERROR_FAIL;
374         }
375
376         gio = g_io_channel_unix_new(system->contr_sock);
377         system->contr_gsource_id =
378                 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
379                                (GIOFunc) __process_contr_reply,
380                                NULL);
381         g_io_channel_unref(gio);
382
383         g_system = system;
384
385         stc_monitor_app_add_by_iface(STC_TOTAL_DATACALL);
386         stc_monitor_app_add_by_iface(STC_TOTAL_WIFI);
387         stc_monitor_app_add_by_iface(STC_TOTAL_BLUETOOTH);
388         stc_monitor_app_add_by_iface(STC_TOTAL_IPV4);
389         stc_monitor_app_add_by_iface(STC_TOTAL_IPV6);
390
391         __update_contr_cb(NULL);
392
393         /* registering periodic kernel counters update callback */
394         g_system->contr_timer_id = g_timeout_add_seconds(CONTR_TIMER_INTERVAL,
395                                                          __update_contr_cb,
396                                                          NULL);
397         if (g_system->contr_timer_id == 0) {
398                 STC_LOGE("Failed to register kernel counters update timer");
399                 __close_contr_sock(g_system);
400                 return STC_ERROR_FAIL;
401         }
402
403         __vconf_get_int(VCONFKEY_STC_BACKGROUND_STATE,
404                         (int *)&g_system->background_state);
405
406         __fill_exceptions_list();
407         stc_monitor_rstns_load();
408
409         __STC_LOG_FUNC_EXIT__;
410         return STC_ERROR_NONE;
411 }
412
413 stc_error_e stc_plugin_monitor_deinitialize(void)
414 {
415         ret_value_msg_if(g_system == NULL, STC_ERROR_FAIL, "stc monitor not initialized!");
416
417         /* close netlink socket for updating kernel counters */
418         __close_contr_sock(g_system);
419
420         /* remove kernel counters update timer */
421         if (g_system->contr_timer_id > 0) {
422                 g_source_remove(g_system->contr_timer_id);
423                 g_system->contr_timer_id = 0;
424         }
425
426         __free_system(g_system);
427         g_system = NULL;
428         g_stop_cb = NULL;
429
430         return STC_ERROR_NONE;
431 }
432
433 GHashTable *stc_monitor_get_system_apps(void)
434 {
435         ret_value_msg_if(g_system == NULL, NULL,
436                                 "stc monitor not initialized!");
437
438         ret_value_msg_if(g_system->apps == NULL, NULL,
439                                 "apps is null!");
440
441         return g_system->apps;
442 }
443
444 GHashTable *stc_monitor_get_system_rstns(void)
445 {
446         ret_value_msg_if(g_system == NULL, NULL,
447                                 "stc monitor not initialized!");
448
449         ret_value_msg_if(g_system->rstns == NULL, NULL,
450                                 "rstns is null!");
451
452         return g_system->rstns;
453 }
454
455 int stc_monitor_get_contr_sock(void)
456 {
457         ret_value_msg_if(g_system == NULL, 0,
458                                 "stc monitor not initialized!");
459
460         return g_system->contr_sock;
461 }
462
463 time_t stc_monitor_get_last_month_ts(void)
464 {
465         ret_value_msg_if(g_system == NULL, 0,
466                                 "stc monitor not initialized!");
467
468         return g_system->last_month_ts;
469 }
470
471 void stc_monitor_set_last_month_ts(time_t time)
472 {
473         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
474
475         g_system->last_month_ts = time;
476 }
477
478 time_t stc_monitor_get_last_week_ts(void)
479 {
480         ret_value_msg_if(g_system == NULL, 0,
481                                 "stc monitor not initialized!");
482
483         return g_system->last_week_ts;
484 }
485
486 void stc_monitor_set_last_week_ts(time_t time)
487 {
488         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
489
490         g_system->last_week_ts = time;
491 }
492
493 time_t stc_monitor_get_last_day_ts(void)
494 {
495         ret_value_msg_if(g_system == NULL, 0,
496                                 "stc monitor not initialized!");
497
498         return g_system->last_day_ts;
499 }
500
501 void stc_monitor_set_last_day_ts(time_t time)
502 {
503         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
504
505         g_system->last_day_ts = time;
506 }
507
508 void stc_monitor_set_rstns_updated(gboolean value)
509 {
510         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
511
512         g_system->rstns_updated = value;
513 }
514
515 gboolean stc_monitor_get_rstns_updated(void)
516 {
517         ret_value_msg_if(g_system == NULL, FALSE,
518                                 "stc monitor not initialized!");
519
520         return g_system->rstns_updated;
521 }
522
523 void stc_monitor_set_apps_updated(gboolean value)
524 {
525         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
526
527         g_system->apps_updated = value;
528 }
529
530 gboolean stc_monitor_get_apps_updated(void)
531 {
532         ret_value_msg_if(g_system == NULL, FALSE,
533                                 "stc monitor not initialized!");
534
535         return g_system->apps_updated;
536 }
537
538 void stc_monitor_set_background_state(gboolean value)
539 {
540         ret_msg_if(g_system == NULL, "stc monitor not initialized!");
541
542         g_system->background_state = value;
543 }
544
545 gboolean stc_monitor_get_background_state(void)
546 {
547         ret_value_msg_if(g_system == NULL, FALSE,
548                                 "stc monitor not initialized!");
549
550         return g_system->background_state;
551 }
552
553 void stc_monitor_update_by_connection(void *data)
554 {
555         stc_connection_s *connection = (stc_connection_s *)data;
556
557         if (connection != NULL && connection->path != NULL) {
558                 iptables_flush_chains();
559
560                 stc_monitor_app_add_by_connection(connection);
561                 stc_monitor_rstn_add_by_connection(connection);
562         }
563 }
564
565 void stc_monitor_add_by_connection(void *data)
566 {
567         stc_connection_s *connection = (stc_connection_s *)data;
568
569         if (connection != NULL && connection->path != NULL) {
570                 stc_monitor_app_add_by_connection(connection);
571                 stc_monitor_rstn_add_by_connection(connection);
572         }
573 }
574
575 void stc_monitor_remove_by_connection(void *data)
576 {
577         stc_connection_s *connection = (stc_connection_s *)data;
578
579         if (connection != NULL && connection->path != NULL) {
580                 stc_monitor_app_remove_by_connection(connection);
581                 stc_monitor_rstn_remove_by_connection(connection);
582         }
583 }
584
585 API stc_plugin_monitor_s stc_plugin_monitor = {
586         .initialize_plugin =
587                 stc_plugin_monitor_initialize,
588         .deinitialize_plugin =
589                 stc_plugin_monitor_deinitialize,
590         .add_application =
591                 stc_plugin_monitor_app_add,
592         .remove_application =
593                 stc_plugin_monitor_app_remove,
594         .lookup_application =
595                 stc_plugin_monitor_app_lookup,
596         .add_restriction =
597                 stc_plugin_monitor_rstn_add,
598         .remove_restriction =
599                 stc_plugin_monitor_rstn_remove,
600         .init_connection =
601                 stc_plugin_monitor_connection_init,
602         .deinit_connection =
603                 stc_plugin_monitor_connection_deinit,
604         .add_process =
605                 stc_plugin_monitor_proc_add,
606         .remove_process =
607                 stc_plugin_monitor_proc_remove,
608         .move_process =
609                 stc_plugin_monitor_proc_move,
610         .update_process_ground =
611                 stc_plugin_monitor_proc_update_ground
612 };