tizen 2.3.1 release
[kernel/api/system-resource.git] / src / network / counter-process.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 /**
20  * @file counter-process.c
21  *
22  * @desc Counter process entity
23  *
24  * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
25  *
26  */
27
28 #include "app-stat.h"
29 #include "cgroup.h"
30 #include "config.h"
31 #include "const.h"
32 #include "counter.h"
33 #include "database.h"
34 #include "datausage-common.h"
35 #include "datausage-quota.h"
36 #include "datausage-quota-processing.h"
37 #include "datausage-restriction.h"
38 #include "edbus-handler.h"
39 #include "generic-netlink.h"
40 #include "net-cls-cgroup.h"
41 #include "nfacct-rule.h"
42 #include "macro.h"
43 #include "module-data.h"
44 #include "notification.h"
45 #include "resourced.h"
46 #include "roaming.h"
47 #include "storage.h"
48 #include "trace.h"
49 #include "transmission.h"
50 #include "datausage-vconf-common.h"
51
52 #include <Ecore.h>
53 #include <endian.h>
54 #include <glib.h>
55 #include <linux/genetlink.h>
56 #include <linux/netlink.h>
57 #include <linux/netfilter/nfnetlink.h>
58 #include <stdbool.h>
59
60 static char *null_str = "(null)";
61
62 #define INSERT_QUERY "REPLACE INTO quotas " \
63         "(binpath, sent_quota, rcv_quota, " \
64         "snd_warning_threshold, rcv_warning_threshold, time_period, " \
65         "start_time, iftype, roaming) " \
66         "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"
67 #define REMOVE_QUOTA "DELETE FROM quotas WHERE binpath=? AND iftype=? " \
68         " AND roaming=?"
69
70 #define QUOTA_CEILING_VALUE 10737418220
71
72 /* Warning threshold part in percent*/
73 enum {
74         WARNING_THRESHOLD_DEFAULT_PART = 5,
75         WARNING_THRESHOLD_PART_10 = 10,
76         WARNING_THRESHOLD_PART_15 = 15,
77         WARNING_THRESHOLD_PART_20 = 20,
78 };
79
80 static sqlite3_stmt *datausage_quota_insert;
81 static sqlite3_stmt *datausage_quota_remove;
82
83 static bool check_net_blocked(sig_atomic_t state)
84 {
85         static int net_blocked; /* counter for run only one time after blocking
86                 to store gap value */
87         if (state & RESOURCED_NET_BLOCKED_STATE &&
88                 net_blocked)
89                 return true;
90
91         /* set net_blocked flag */
92         if (!net_blocked &&
93                         state & RESOURCED_NET_BLOCKED_STATE)
94                 ++net_blocked;
95         /* reset net_blocked flag */
96         if (net_blocked &&
97                 !(state & RESOURCED_NET_BLOCKED_STATE))
98                 --net_blocked;
99         _D("net_blocked %d, state %d", net_blocked, state);
100         return false;
101 }
102
103 #ifdef CONFIG_DATAUSAGE_NFACCT
104 static Eina_Bool send_counter_request(struct counter_arg *carg)
105 {
106         return nfacct_send_get(carg) == RESOURCED_ERROR_NONE ?
107                 ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
108 }
109
110 /* TODO exclude wlll be broken */
111 static nfacct_rule_jump get_counter_jump(nfacct_rule_intend intend)
112 {
113         if (intend == NFACCT_WARN)
114                 return NFACCT_JUMP_ACCEPT;
115         else if (intend == NFACCT_BLOCK)
116                 return NFACCT_JUMP_REJECT;
117
118         return NFACCT_JUMP_UNKNOWN;
119 }
120
121 static void populate_counters(char *cnt_name,
122                         struct counter_arg *carg)
123 {
124         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
125         nfacct_rule_jump jump = NFACCT_JUMP_UNKNOWN;
126
127         if (!recreate_counter_by_name(cnt_name, &counter)) {
128                 _E("Can't parse counter name %s", cnt_name);
129                 return;
130         }
131
132         counter.carg = carg;
133         strncpy(counter.name, cnt_name, sizeof(counter.name)-1);
134         jump = get_counter_jump(counter.intend);
135         _D("counter: %s, classid %u, iftype %u, iotype %d, bytes %lu", cnt_name,
136                 counter.classid, counter.iftype,
137                 counter.iotype);
138
139         produce_net_rule(&counter, 0, 0,
140                 NFACCT_ACTION_APPEND, jump, counter.iotype);
141
142         keep_counter(&counter);
143 }
144
145 static void populate_traf_stat_list(char *cnt_name, uint64_t bytes,
146                         struct counter_arg *carg)
147 {
148         struct traffic_stat *to_insert;
149         struct classid_iftype_key *key;
150         struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
151         traffic_stat_tree *tree = NULL;
152
153         _D("cnt_name %s", cnt_name);
154
155         if (!recreate_counter_by_name(cnt_name, &counter)) {
156                 _E("Can't parse counter name %s", cnt_name);
157                 return;
158         }
159
160         _D("classid %u, iftype %u, iotype %d, intend %d, ifname %s, bytes %lu",
161            counter.classid, counter.iftype, counter.iotype, counter.intend, counter.ifname, bytes);
162
163         if (counter.iotype == NFACCT_COUNTER_UNKNOWN ||
164                 counter.intend != NFACCT_COUNTER) {
165                 _E("Counter type is not supported!");
166                 return;
167         }
168
169         tree = counter.iotype == NFACCT_COUNTER_IN ? carg->in_tree : carg->out_tree;
170         to_insert = g_new(struct traffic_stat, 1);
171         if (!to_insert) {
172                 _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat));
173                 return;
174         }
175
176         key = g_new(struct classid_iftype_key, 1);
177
178         if (!key) {
179                 _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key));
180                 g_free((gpointer)to_insert);
181                 return;
182         }
183
184         to_insert->bytes = bytes;
185         /*to_insert->ifindex = cur->ifindex;*/
186         key->classid = counter.classid;
187         key->iftype = counter.iftype;
188         STRING_SAVE_COPY(key->ifname, counter.ifname);
189         g_tree_insert((GTree *) tree, (gpointer)key, to_insert);
190 }
191
192 static int fill_counters(struct rtattr *attr_list[__NFACCT_MAX],
193                 void *user_data)
194 {
195         struct counter_arg *carg = user_data;
196         char *cnt_name = (char *)RTA_DATA(
197                                 attr_list[NFACCT_NAME]);
198         if (carg->initiate)
199                 populate_counters(cnt_name, carg);
200         else {
201                 uint64_t *bytes_p = (uint64_t *)RTA_DATA(attr_list[NFACCT_BYTES]);
202                 int bytes = be64toh(*bytes_p);
203                 /* TODO: optimize at kernel level, kernel should not send counter
204                  * in case of 0 bytes, it's necessary to introduce new NFACCT_*
205                  * command */
206                 if (bytes)
207                         populate_traf_stat_list(cnt_name, bytes, carg);
208         }
209
210         return 0;
211 }
212
213 static int post_fill_counters(void *user_data)
214 {
215         struct counter_arg *carg = user_data;
216
217         if (carg->initiate)
218                 carg->initiate = 0;
219
220         return 0;
221 }
222
223 #else
224 static Eina_Bool send_counter_request(struct counter_arg *carg)
225 {
226         int ret = send_command(carg->sock, carg->pid, carg->family_id_stat,
227                         TRAF_STAT_C_GET_CONN_IN);
228         ret_value_msg_if(ret < 0, ECORE_CALLBACK_RENEW,
229                         "Failed to send command to get incomming traffic");
230
231         ret = send_command(carg->sock, carg->pid, carg->family_id_stat,
232                         TRAF_STAT_C_GET_PID_OUT);
233         ret_value_msg_if(ret < 0, ECORE_CALLBACK_RENEW,
234                         "Failed to send command to get outgoing traffic");
235
236         return ECORE_CALLBACK_RENEW;
237 }
238 #endif /* CONFIG_DATAUSAGE_NFACCT */
239
240 static Eina_Bool _counter_func_cb(void *user_data)
241 {
242         struct counter_arg *carg = (struct counter_arg *)user_data;
243
244         if (check_net_blocked(carg->opts->state)) {
245                 ecore_timer_freeze(carg->ecore_timer);
246                 return ECORE_CALLBACK_RENEW;
247         }
248
249         if (!(carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)) {
250                 /* Here we just sent command,
251                  * answer we receiving in another callback, send_command uses
252                  * return value the same as sendto
253                  */
254
255                 return send_counter_request(carg);
256         }
257
258         close(carg->sock);
259         return ECORE_CALLBACK_CANCEL;
260 }
261
262 static dbus_bool_t deserialize_restriction(
263         DBusMessage *msg, char **appid, resourced_net_restrictions *rest,
264         enum traffic_restriction_type *rst_type)
265 {
266         DBusError err;
267         dbus_error_init(&err);
268
269         int ret = dbus_message_get_args(
270                 msg, &err,
271                 DBUS_TYPE_STRING, appid,
272                 DBUS_TYPE_INT32, rst_type,
273                 DBUS_TYPE_INT32, &(rest->rs_type),
274                 DBUS_TYPE_INT32, &(rest->iftype),
275                 DBUS_TYPE_INT32, &(rest->send_limit),
276                 DBUS_TYPE_INT32, &(rest->rcv_limit),
277                 DBUS_TYPE_INT32, &(rest->snd_warning_limit),
278                 DBUS_TYPE_INT32, &(rest->rcv_warning_limit),
279                 DBUS_TYPE_INT32, &(rest->roaming),
280                 DBUS_TYPE_INVALID);
281
282         if (ret == FALSE) {
283                 _E("Can't deserialize quota! [%s:%s]\n",
284                 err.name, err.message);
285         }
286
287         dbus_error_free(&err);
288
289         return ret;
290 }
291
292 static DBusMessage *edbus_process_restriction(E_DBus_Object *obj,
293                                               DBusMessage *msg)
294 {
295         DBusMessageIter iter;
296         DBusMessage *reply;
297         int ret;
298         resourced_ret_c dbus_ret = RESOURCED_ERROR_NONE;
299         char *appid = NULL;
300         resourced_net_restrictions rest;
301         enum traffic_restriction_type rst_type;
302
303         ret = dbus_message_is_method_call(
304             msg, RESOURCED_INTERFACE_NETWORK,
305             RESOURCED_NETWORK_PROCESS_RESTRICTION);
306
307         if (ret == FALSE)
308                 return dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD,
309                                               "Method is not supported");
310
311         ret = deserialize_restriction(msg, &appid, &rest, &rst_type);
312
313         reply = dbus_message_new_method_return(msg);
314         dbus_message_iter_init_append(reply, &iter);
315         if (ret == FALSE) {
316                 dbus_ret = RESOURCED_ERROR_FAIL;
317                 goto out;
318         }
319
320         dbus_ret = proc_keep_restriction(appid, NONE_QUOTA_ID, &rest,
321                                              rst_type);
322 out:
323         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &dbus_ret);
324
325         return reply;
326 }
327
328 static DBusMessage *edbus_update_counters(E_DBus_Object *obj, DBusMessage *msg)
329 {
330         DBusMessage *reply;
331         struct shared_modules_data *m_data = get_shared_modules_data();
332
333         if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
334                                         RESOURCED_NETWORK_UPDATE) == 0)
335                 return dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD,
336                                               "Method is not supported");
337
338         if (m_data != NULL && m_data->carg != NULL) {
339                 if (!(m_data->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE))
340                         m_data->carg->opts->state |=
341                                 RESOURCED_FORCIBLY_FLUSH_STATE;
342
343                 /* postpone periodic update on one minute */
344                 reschedule_count_timer(m_data->carg, COUNTER_UPDATE_PERIOD);
345                 _counter_func_cb(m_data->carg);
346         }
347
348         reply = dbus_message_new_method_return(msg);
349         return reply;
350 }
351
352 struct serialization_quota {
353         int time_period;
354         int64_t snd_quota;
355         int64_t rcv_quota;
356         int snd_warning_threshold;
357         int rcv_warning_threshold;
358         resourced_state_t quota_type;
359         resourced_iface_type iftype;
360         time_t start_time;
361         resourced_roaming_type roaming_type;
362 };
363
364 static inline int _get_threshold_part(int time_period)
365 {
366         if (time_period < RESOURCED_PERIOD_DAY)
367                 return WARNING_THRESHOLD_PART_20;
368
369         if (time_period < RESOURCED_PERIOD_WEEK)
370                 return WARNING_THRESHOLD_PART_15;
371
372         if (time_period < RESOURCED_PERIOD_MONTH)
373                 return WARNING_THRESHOLD_PART_10;
374
375         return WARNING_THRESHOLD_DEFAULT_PART;
376 }
377
378 static inline int64_t get_quota_ceiling(const int64_t quota)
379 {
380         return quota >= QUOTA_CEILING_VALUE ? QUOTA_CEILING_VALUE :
381                 quota;
382 }
383
384 static inline int _evaluate_warning_threshold(const int64_t quota,
385         const int time_period, const int user_threshold)
386 {
387         int threshold_part = WARNING_THRESHOLD_DEFAULT_PART;
388
389         if (user_threshold != WARNING_THRESHOLD_DEFAULT)
390                 return user_threshold;
391
392         threshold_part = _get_threshold_part(time_period);
393
394         return (get_quota_ceiling(quota) / 100 ) * threshold_part;
395 }
396
397 static dbus_bool_t deserialize_quota(
398         DBusMessage *msg, char **appid,
399         struct serialization_quota *quota)
400 {
401         DBusError err;
402         dbus_error_init(&err);
403
404         int ret = dbus_message_get_args(
405                 msg, &err,
406                 DBUS_TYPE_STRING, appid,
407                 DBUS_TYPE_INT32, &quota->time_period,
408                 DBUS_TYPE_UINT64, &quota->snd_quota,
409                 DBUS_TYPE_UINT64, &quota->rcv_quota,
410                 DBUS_TYPE_INT32, &quota->snd_warning_threshold,
411                 DBUS_TYPE_INT32, &quota->rcv_warning_threshold,
412                 DBUS_TYPE_INT32, &quota->quota_type,
413                 DBUS_TYPE_INT32, &quota->iftype,
414                 DBUS_TYPE_INT32, &quota->start_time,
415                 DBUS_TYPE_INT32, &quota->roaming_type,
416                 DBUS_TYPE_INVALID);
417         if (ret == FALSE) {
418                 _E("Can't deserialize set quota message ![%s:%s]\n",
419                 err.name, err.message);
420         }
421
422         dbus_error_free(&err);
423
424         quota->iftype = (quota->iftype == RESOURCED_IFACE_UNKNOWN) ?
425                         RESOURCED_IFACE_ALL : quota->iftype;
426
427         quota->snd_warning_threshold = _evaluate_warning_threshold(
428                 quota->snd_quota, quota->time_period,
429                 quota->snd_warning_threshold);
430         quota->rcv_warning_threshold = _evaluate_warning_threshold(
431                 quota->rcv_quota, quota->time_period,
432                 quota->rcv_warning_threshold);
433
434 return ret;
435 }
436
437 static dbus_bool_t deserialize_remove_quota(
438         DBusMessage *msg, char **appid,
439         resourced_iface_type *iftype, resourced_roaming_type *roaming)
440 {
441         DBusError err;
442         dbus_error_init(&err);
443
444         int ret = dbus_message_get_args(
445                 msg, &err,
446                 DBUS_TYPE_STRING, appid,
447                 DBUS_TYPE_INT32, iftype,
448                 DBUS_TYPE_INT32, roaming,
449                 DBUS_TYPE_INVALID);
450         if (ret == FALSE) {
451                 _E("Can't deserialize remove quota message! [%s:%s]\n",
452                 err.name, err.message);
453         }
454
455         dbus_error_free(&err);
456
457         return ret;
458 }
459
460 static DBusMessage *edbus_join_net_stat(E_DBus_Object *obj, DBusMessage *msg)
461 {
462         char *app_id = NULL;
463         int pid = 0;
464         resourced_ret_c ret = RESOURCED_ERROR_NONE;
465         DBusMessage *reply;
466         DBusMessageIter iter;
467         DBusError err;
468
469         dbus_error_init(&err);
470
471         if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
472                                 RESOURCED_NETWORK_JOIN_NET_STAT) == 0) {
473                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
474                 goto join_net_out;
475         }
476
477         ret = dbus_message_get_args(
478                 msg, &err,
479                 DBUS_TYPE_STRING, &app_id,
480                 DBUS_TYPE_INT32, &pid,
481                 DBUS_TYPE_INVALID);
482         if (ret == FALSE) {
483                 _E("Can't deserialize join netstat message! [%s:%s]\n",
484                 err.name, err.message);
485                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
486                 goto join_net_out;
487         }
488
489         ret = join_net_cls(app_id, pid);
490
491 join_net_out:
492         reply = dbus_message_new_method_return(msg);
493         dbus_message_iter_init_append(reply, &iter);
494         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
495
496         dbus_error_free(&err);
497         return reply;
498 }
499
500 static int init_datausage_quota_remove(sqlite3 *db)
501 {
502         int rc;
503
504         if (datausage_quota_remove)
505                 return SQLITE_OK;
506
507          rc = sqlite3_prepare_v2(db, REMOVE_QUOTA, -1,
508                         &datausage_quota_remove, NULL);
509         if (rc != SQLITE_OK) {
510                 _E("can not prepare datausage_quota_remove");
511                 datausage_quota_remove = NULL;
512                 sqlite3_finalize(datausage_quota_remove);
513                 return rc;
514         }
515
516         return rc;
517 }
518
519 static resourced_ret_c remove_quota(const char *app_id,
520         resourced_iface_type iftype, resourced_roaming_type roaming)
521 {
522         resourced_ret_c error_code = RESOURCED_ERROR_NONE;
523         libresourced_db_initialize_once();
524
525         if (init_datausage_quota_remove(resourced_get_database()) != SQLITE_OK) {
526                 _D("Failed to initialize data usage quota statements: %s\n",
527                    sqlite3_errmsg(resourced_get_database()));
528                 return RESOURCED_ERROR_DB_FAILED;
529         }
530
531         if (sqlite3_bind_text(datausage_quota_remove, 1, app_id, -1, SQLITE_STATIC) !=
532             SQLITE_OK) {
533                 _SE("Can not bind app_id: %s for preparing statement",
534                    app_id);
535                 error_code =  RESOURCED_ERROR_DB_FAILED;
536                 goto out;
537         }
538
539         if (sqlite3_bind_int(datausage_quota_remove, 2, iftype)
540             != SQLITE_OK) {
541                 _E("Can not bind iftype:%d for preparing statement",
542                         iftype);
543                 error_code =  RESOURCED_ERROR_DB_FAILED;
544                 goto out;
545         }
546
547         if (sqlite3_bind_int(datausage_quota_remove, 3, roaming)
548             != SQLITE_OK) {
549                 _E("Can not bind iftype:%d for preparing statement",
550                         roaming);
551                 error_code =  RESOURCED_ERROR_DB_FAILED;
552                 goto out;
553         }
554
555         if (sqlite3_step(datausage_quota_remove) != SQLITE_DONE) {
556                 _E("failed to remove record");
557                 error_code =  RESOURCED_ERROR_DB_FAILED;
558                 goto out;
559         }
560
561         restriction_set_status(RESTRICTION_STATE_UNSET);
562
563         _SD("quota for app %s removed", app_id);
564
565 out:
566         sqlite3_reset(datausage_quota_remove);
567         return error_code;
568 }
569
570 static DBusMessage *edbus_remove_quota(E_DBus_Object *obj, DBusMessage *msg)
571 {
572         char *app_id = NULL;
573         resourced_iface_type iftype;
574         resourced_ret_c ret = RESOURCED_ERROR_NONE;
575         resourced_roaming_type roaming;
576         DBusMessage *reply;
577         DBusMessageIter iter;
578
579         if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
580                                 RESOURCED_NETWORK_REMOVE_QUOTA) == 0) {
581                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
582                 goto remove_out;
583         }
584
585         if (deserialize_remove_quota(msg, &app_id, &iftype, &roaming)
586                              == FALSE) {
587                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
588                 goto remove_out;
589         }
590
591         ret = remove_quota(app_id, iftype, roaming);
592         update_quota_state(app_id, iftype, 0, 0, roaming);
593
594 remove_out:
595         reply = dbus_message_new_method_return(msg);
596         dbus_message_iter_init_append(reply, &iter);
597         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
598         return reply;
599 }
600
601 static int init_datausage_quota_insert(sqlite3 *db)
602 {
603         int rc;
604
605         if (datausage_quota_insert)
606                 return SQLITE_OK;
607
608         rc = sqlite3_prepare_v2(db, INSERT_QUERY,
609                                     -1, &datausage_quota_insert, NULL);
610
611         if (rc != SQLITE_OK) {
612                 _E("can not prepare datausage_quota_insert");
613                 datausage_quota_insert = NULL;
614                 sqlite3_finalize(datausage_quota_insert);
615         }
616
617         return rc;
618 }
619
620 static resourced_ret_c store_quota(const char *app_id,
621         const struct serialization_quota *quota)
622 {
623         resourced_ret_c error_code = RESOURCED_ERROR_NONE;
624
625         libresourced_db_initialize_once();
626
627         if (init_datausage_quota_insert(resourced_get_database()) != SQLITE_OK) {
628                 _D("Failed to initialize data usage quota statements: %s\n",
629                    sqlite3_errmsg(resourced_get_database()));
630                 return RESOURCED_ERROR_DB_FAILED;
631         }
632
633         if (sqlite3_bind_text(datausage_quota_insert, 1, app_id, -1,
634                 SQLITE_STATIC) != SQLITE_OK) {
635                 _SE("Can not bind app_id: %s for prepearing statement: %s",
636                         app_id, sqlite3_errmsg(resourced_get_database()));
637                 error_code = RESOURCED_ERROR_DB_FAILED;
638                 goto out;
639         }
640
641         if (sqlite3_bind_int64(datausage_quota_insert, 2,
642                 quota->snd_quota) != SQLITE_OK) {
643                 _E("Can not bind snd_quota: %lld for preparing statement",
644                         quota->snd_quota);
645                 error_code = RESOURCED_ERROR_DB_FAILED;
646                 goto out;
647         }
648
649         if (sqlite3_bind_int64(datausage_quota_insert, 3,
650                 quota->rcv_quota) != SQLITE_OK) {
651                 _E("Can not bind rcv_quota: %lld for preparing statement",
652                         quota->rcv_quota);
653                 error_code = RESOURCED_ERROR_DB_FAILED;
654                 goto out;
655         }
656
657         if (sqlite3_bind_int64(datausage_quota_insert, 4,
658                 quota->snd_warning_threshold) != SQLITE_OK) {
659                 _E("Can not bind snd_warning_threshold: %lld for preparing statement",
660                         quota->snd_warning_threshold);
661                 error_code = RESOURCED_ERROR_DB_FAILED;
662                 goto out;
663         }
664
665         if (sqlite3_bind_int64(datausage_quota_insert, 5,
666                 quota->rcv_warning_threshold) != SQLITE_OK) {
667                 _E("Can not bind rcv_warning_threshold: %lld for preparing statement",
668                         quota->rcv_warning_threshold);
669                 error_code = RESOURCED_ERROR_DB_FAILED;
670                 goto out;
671         }
672
673         if (sqlite3_bind_int64(datausage_quota_insert, 6,
674                 quota->time_period) != SQLITE_OK) {
675                 _E("Can not bind time_period: %d for preparing statement",
676                         quota->time_period);
677                 error_code = RESOURCED_ERROR_DB_FAILED;
678                 goto out;
679         }
680
681         if (sqlite3_bind_int(datausage_quota_insert, 7,
682                 quota->start_time) != SQLITE_OK) {
683                 _E("Can not bind start_time: %d for preparing statement",
684                         quota->start_time);
685                 error_code = RESOURCED_ERROR_DB_FAILED;
686                 goto out;
687         }
688
689         if (sqlite3_bind_int(datausage_quota_insert, 8,
690                 quota->iftype) != SQLITE_OK) {
691                 _E("Can not bind iftype: %d for preparing statement",
692                         quota->iftype);
693                 error_code = RESOURCED_ERROR_DB_FAILED;
694                 goto out;
695         }
696
697         if (sqlite3_bind_int(datausage_quota_insert, 9,
698                 quota->roaming_type) != SQLITE_OK) {
699                 _E("Can not bind start_time: %d for preparing statement",
700                         quota->start_time);
701                 error_code = RESOURCED_ERROR_DB_FAILED;
702                 goto out;
703         }
704
705         if (sqlite3_step(datausage_quota_insert) != SQLITE_DONE) {
706                 _E("Failed to record quota %s.",
707                         sqlite3_errmsg(resourced_get_database()));
708                 error_code = RESOURCED_ERROR_DB_FAILED;
709                 goto out;
710         }
711 out:
712         sqlite3_reset(datausage_quota_insert);
713         return error_code;
714 }
715
716 static DBusMessage *edbus_create_quota(E_DBus_Object *obj, DBusMessage *msg)
717 {
718         DBusMessage *reply;
719         DBusMessageIter iter;
720
721         char *app_id = NULL;
722         struct serialization_quota quota;
723         struct shared_modules_data *m_data = get_shared_modules_data();
724         struct counter_arg *carg;
725         resourced_ret_c ret = RESOURCED_ERROR_NONE;
726
727         if (!m_data || !m_data->carg) {
728                 _E("Not enough local parameters: modules data %p, counter arg %p",
729                         m_data, m_data->carg);
730                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
731                 goto update_out;
732         }
733
734         carg = m_data->carg;
735
736         if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
737                                         RESOURCED_NETWORK_CREATE_QUOTA) == 0) {
738                 _E("Invalid DBUS argument");
739                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
740                 goto update_out;
741         }
742
743         deserialize_quota(msg, &app_id, &quota);
744         ret = store_quota(app_id, &quota);
745         if (ret != RESOURCED_ERROR_NONE) {
746                 _E("Can't store quota!");
747                 goto update_out;
748         }
749
750         update_quota_state(app_id, quota.iftype, quota.start_time,
751                 quota.time_period, quota.roaming_type);
752
753         ret_value_msg_if(!carg->opts,
754                 dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS,
755                                       "Counter args is not provided"),
756                          "Please provide valid argument!");
757
758         carg->opts->is_update_quota = 1;
759         reschedule_count_timer(carg, 0);
760         _SD("Datausage quota changed");
761
762 update_out:
763         reply = dbus_message_new_method_return(msg);
764         dbus_message_iter_init_append(reply, &iter);
765         dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
766         return reply;
767 }
768
769 struct get_stats_context {
770         DBusMessage *reply;
771         DBusMessage *msg;
772         int info_count;
773         GSList *infos;
774         DBusMessageIter iter;
775 };
776
777 static resourced_cb_ret answer_get_stat(const data_usage_info *info,
778                                                void *user_data)
779 {
780         struct get_stats_context *ctx = (struct get_stats_context *)user_data;
781         data_usage_info *insert = (data_usage_info *)malloc(sizeof(data_usage_info));
782
783         ret_value_msg_if(insert == NULL, RESOURCED_CANCEL, "Can't allocate memory!");
784         memcpy(insert, info, sizeof(data_usage_info));
785         if (info->app_id) {
786                 int app_id_len = strlen(info->app_id) + 1;
787                 insert->app_id = (char *)malloc(app_id_len);
788                 if (!insert->app_id) {
789                         free(insert);
790                         _E("Malloc of answer_get_stat failed\n");
791                         return RESOURCED_CANCEL;
792                 }
793
794                 strncpy((char *)insert->app_id, info->app_id, app_id_len);
795         }
796         ctx->infos = g_slist_append(ctx->infos, insert);
797         return RESOURCED_CONTINUE;
798 }
799
800 static void prepare_response(struct get_stats_context *ctx)
801 {
802         GSList *iter;
803         data_usage_info *info;
804         DBusMessageIter arr;
805
806         ctx->reply = dbus_message_new_method_return(ctx->msg);
807         dbus_message_iter_init_append(ctx->reply, &ctx->iter);
808         dbus_message_iter_open_container(&ctx->iter, DBUS_TYPE_ARRAY, "(siiiiiii)", &arr);
809
810         gslist_for_each_item(iter, ctx->infos) {
811                 info = (data_usage_info *)iter->data;
812
813                 DBusMessageIter sub;
814
815                 dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
816                 if (info->app_id == NULL)
817                         dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
818                                 &null_str);
819                 else
820                         dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
821                                 &info->app_id);
822                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->iftype);
823                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->interval->from);
824                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->interval->to);
825                 /* incoming bytes */
826                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.incoming_bytes);
827                 /* outgoing bytes */
828                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.outgoing_bytes);
829
830                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->roaming);
831                 dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->hw_net_protocol_type);
832
833                 dbus_message_iter_close_container(&arr, &sub);
834         }
835
836         dbus_message_iter_close_container(&ctx->iter, &arr);
837         g_slist_free_full(ctx->infos, free);
838 }
839
840 static void deserialize_rule(DBusMessage *msg, data_usage_selection_rule *rule, char **app_id)
841 {
842         DBusError err;
843         dbus_error_init(&err);
844
845         int ret = dbus_message_get_args(
846                 msg, &err,
847                 DBUS_TYPE_STRING, app_id,
848                 DBUS_TYPE_INT32, &rule->from,
849                 DBUS_TYPE_INT32, &rule->to,
850                 DBUS_TYPE_INT32, &rule->iftype,
851                 DBUS_TYPE_INT32, &rule->granularity,
852                 DBUS_TYPE_INVALID);
853
854         if (ret == FALSE) {
855                 _E("Can't deserialize quota! [%s:%s]\n",
856                         err.name, err.message);
857         }
858
859         if (app_id && !strcmp(*app_id, null_str))
860                 *app_id = NULL;
861         dbus_error_free(&err);
862 }
863
864 static DBusMessage *edbus_get_stats(E_DBus_Object *obj, DBusMessage *msg)
865 {
866         data_usage_selection_rule rule;
867         char *app_id = NULL;
868         resourced_ret_c ret;
869         struct get_stats_context ctx;
870         ctx.infos = NULL;
871
872         if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
873                                         RESOURCED_NETWORK_GET_STATS) == 0) {
874                 ret = RESOURCED_ERROR_INVALID_PARAMETER;
875                 goto update_out;
876         }
877
878         _SD("Datausage get stats");
879         ctx.msg = msg;
880         deserialize_rule(msg, &rule, &app_id);
881         if (app_id)
882                 ret = data_usage_details_foreach(app_id, &rule, answer_get_stat,
883                         &ctx);
884         else
885                 ret = data_usage_foreach(&rule, answer_get_stat, &ctx);
886
887         prepare_response(&ctx);
888         return ctx.reply;
889
890 update_out:
891         ctx.reply = dbus_message_new_method_return(msg);
892         dbus_message_iter_init_append(ctx.reply, &ctx.iter);
893         dbus_message_iter_append_basic(&ctx.iter, DBUS_TYPE_INT32, &ret);
894         return ctx.reply;
895 }
896
897 struct nl_family_params {
898         struct genl *ans;
899         struct counter_arg *carg;
900 };
901
902 typedef struct {
903         struct nl_family_params params;
904         void (*process)(struct nl_family_params *params);
905 } nl_serialization_command;
906
907 static inline char *_get_public_appid(const uint32_t classid)
908 {
909         char *appid;
910
911         /* following value for ALL is suitable for using in statistics
912            what's why it's not in get_app_id_by_classid */
913         if (classid == RESOURCED_ALL_APP_CLASSID)
914                 return RESOURCED_ALL_APP;
915
916         appid = get_app_id_by_classid(classid, true);
917         return !appid ? UNKNOWN_APP : appid;
918 }
919
920 static bool need_flush_immediatelly(sig_atomic_t state)
921 {
922         return state & RESOURCED_FORCIBLY_FLUSH_STATE ||
923                 state & RESOURCED_FORCIBLY_QUIT_STATE;
924 }
925
926 static Eina_Bool _store_and_free_result_cb(void *user_data)
927 {
928         struct counter_arg *arg = (struct counter_arg *)user_data;
929
930         ret_value_msg_if(!arg, ECORE_CALLBACK_CANCEL, "Please provide valid argument!");
931
932         if (store_result(arg->result, need_flush_immediatelly(arg->opts->state)
933                          ? 0 : arg->opts->flush_period)) {
934                 /*We still plan to use result outside, just
935                 remove and free elements */
936                 g_tree_ref(arg->result->tree);
937                 free_app_stat_tree(arg->result);
938                 if (arg->opts->state & RESOURCED_FORCIBLY_FLUSH_STATE) {
939                         arg->opts->state &= ~RESOURCED_FORCIBLY_FLUSH_STATE;
940                         if (broadcast_edbus_signal(
941                                     RESOURCED_PATH_NETWORK,
942                                     RESOURCED_INTERFACE_NETWORK,
943                                     RESOURCED_NETWORK_UPDATE_FINISH,
944                                     DBUS_TYPE_INVALID, NULL))
945                                 _E("Failed to send DBUS message\n");
946                 }
947         }
948
949         arg->store_result_timer = NULL;
950         return ECORE_CALLBACK_CANCEL;
951 }
952
953 static void _store_and_free_result(struct counter_arg *arg)
954 {
955         if (!arg->store_result_timer)
956                 arg->store_result_timer = ecore_timer_add(STORE_DELAY_INTERVAL,
957                                            _store_and_free_result_cb, arg);
958 }
959
960 static void _process_network_counter(struct nl_family_params *params)
961 {
962         resourced_ret_c ret;
963         struct netlink_serialization_params ser_params = {
964                 .carg = params->carg,
965                 .ans = params->ans,
966 #ifdef CONFIG_DATAUSAGE_NFACCT
967                 .eval_attr = fill_counters,
968                 .post_eval_attr = post_fill_counters,
969 #endif
970         };
971
972         netlink_serialization_command *netlink =
973                 netlink_create_command(&ser_params);
974
975         if (!netlink) {
976                 _E("Can not create command");
977                 return;
978         }
979
980         netlink->deserialize_answer(&(netlink->params));
981
982         /* process only filled in/out or tethering traffic */
983         if ((!g_tree_nnodes(params->carg->in_tree) ||
984              !g_tree_nnodes(params->carg->out_tree)) &&
985             params->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)
986                 return;
987
988         pthread_rwlock_wrlock(&params->carg->result->guard);
989         ret = prepare_application_stat(params->carg->in_tree,
990                         params->carg->out_tree, params->carg->result,
991                 params->carg->opts);
992         pthread_rwlock_unlock(&params->carg->result->guard);
993
994         if (ret != RESOURCED_ERROR_NONE) {
995                 _E("Failed to prepare application statistics!");
996                 return;
997         }
998         ret = process_quota(params->carg->result, params->carg->opts);
999         if (ret != 0) {
1000                 _E("Failed to process quota!");
1001                 return;
1002         }
1003
1004         _store_and_free_result(params->carg);
1005
1006         g_tree_ref(params->carg->out_tree);
1007         free_traffic_stat_tree(params->carg->out_tree);
1008         g_tree_ref(params->carg->in_tree);
1009         free_traffic_stat_tree(params->carg->in_tree);
1010 }
1011
1012 #ifdef CONFIG_DATAUSAGE_NFACCT
1013 static resourced_ret_c choose_netlink_process(struct genl *ans, nl_serialization_command *command,
1014         struct counter_arg *carg)
1015 {
1016         command->process = _process_network_counter;
1017         return RESOURCED_ERROR_NONE;
1018 }
1019 #else
1020
1021 static void _process_restriction(struct nl_family_params *cmd)
1022 {
1023         struct traffic_restriction restriction = {0,};
1024         uint8_t notification_type = RESTRICTION_NOTI_C_UNSPEC;
1025         char *app_id = NULL;
1026         resourced_iface_type iftype;
1027         resourced_restriction_info rst_info = {0,};
1028         resourced_ret_c ret;
1029
1030         _D("Restriction notification");
1031
1032         if (process_netlink_restriction_msg(cmd->ans, &restriction,
1033             &notification_type) !=
1034             RESOURCED_ERROR_NONE) {
1035                 _E("Failed to process netlink restriction.");
1036                 return;
1037         }
1038
1039         app_id = _get_public_appid(restriction.sk_classid);
1040         iftype = get_iftype(restriction.ifindex);
1041
1042         ret = get_restriction_info(app_id, iftype, &rst_info);
1043         ret_msg_if(ret != RESOURCED_ERROR_NONE,
1044                 "Failed to get restriction info!");
1045
1046         if (notification_type == RESTRICTION_NOTI_C_ACTIVE) {
1047                 if (rst_info.quota_id != NONE_QUOTA_ID)
1048                         send_restriction_notification(app_id);
1049                 update_restriction_db(app_id, iftype, 0, 0,
1050                                       RESOURCED_RESTRICTION_ACTIVATED,
1051                 rst_info.quota_id, rst_info.roaming);
1052         } else if (notification_type == RESTRICTION_NOTI_C_WARNING) {
1053                 /* nested if due error message correctness */
1054                 if (rst_info.quota_id != NONE_QUOTA_ID)
1055                         send_restriction_warn_notification(app_id);
1056         } else
1057                 _E("Unkown restriction notification type");
1058 }
1059
1060 static resourced_ret_c choose_netlink_process(struct genl *ans,
1061         nl_serialization_command *command, struct counter_arg *carg)
1062 {
1063         int family = netlink_get_family(ans);
1064
1065         if (family == carg->family_id_restriction)
1066                 command->process = _process_restriction;
1067         else if (family == carg->family_id_stat)
1068                 command->process = _process_network_counter;
1069         else {
1070                 _E("General netlink family %d unsupported!", family);
1071                 return RESOURCED_ERROR_NO_DATA;
1072         }
1073         return RESOURCED_ERROR_NONE;
1074 }
1075 #endif /* CONFIG_DATAUSAGE_NFACCT */
1076
1077 static nl_serialization_command *choose_handler(struct genl *ans,
1078         struct counter_arg *carg)
1079 {
1080         static nl_serialization_command command;
1081         resourced_ret_c ret;
1082
1083         if (!ans || !carg) {
1084                 _E("Please provide valid pointer!");
1085                 return NULL;
1086         }
1087
1088         if (!command.params.carg)
1089                 command.params.carg = carg;
1090         command.params.ans = ans;
1091
1092         ret = choose_netlink_process(ans, &command, carg);
1093         ret_value_msg_if(ret != RESOURCED_ERROR_NONE, NULL,
1094                 "Could not choose proper netlink process function! \n");
1095
1096         return &command;
1097 }
1098
1099 static Eina_Bool _answer_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
1100 {
1101         struct counter_arg *carg = (struct counter_arg *)user_data;
1102         struct genl ans;
1103         nl_serialization_command *netlink_handler = NULL;
1104         int ret;
1105
1106         ret = read_netlink(carg->sock, &ans, sizeof(struct genl));
1107         if (ret == 0)
1108                 goto out;
1109         carg->ans_len = ret;
1110         netlink_handler = choose_handler(&ans, carg);
1111
1112         if (!netlink_handler)
1113                 goto out;
1114
1115         netlink_handler->process(&(netlink_handler->params));
1116
1117 out:
1118         return ECORE_CALLBACK_RENEW;
1119 }
1120
1121 static const struct edbus_method edbus_methods[] = {
1122         { RESOURCED_NETWORK_UPDATE, NULL, NULL, edbus_update_counters },
1123         { RESOURCED_NETWORK_PROCESS_RESTRICTION, NULL, NULL,
1124           edbus_process_restriction },
1125         { RESOURCED_NETWORK_CREATE_QUOTA, NULL, NULL, edbus_create_quota },
1126         { RESOURCED_NETWORK_REMOVE_QUOTA, NULL, NULL, edbus_remove_quota },
1127         { RESOURCED_NETWORK_JOIN_NET_STAT, NULL, NULL, edbus_join_net_stat },
1128         { RESOURCED_NETWORK_GET_STATS, "siiii", "a(siiiiiii)", edbus_get_stats },
1129 };
1130
1131 #ifdef CONFIG_DATAUSAGE_NFACCT
1132 int init_sock(struct counter_arg *carg)
1133 {
1134         carg->sock = create_netlink(NETLINK_NETFILTER, 0);
1135         return carg->sock != 0 ? RESOURCED_ERROR_NONE :
1136                 RESOURCED_ERROR_FAIL;
1137 }
1138 #else
1139 int init_sock(struct counter_arg *carg)
1140 {
1141         int error = RESOURCED_ERROR_NONE;
1142         carg->sock = create_netlink(NETLINK_GENERIC, 0);
1143
1144         ret_value_msg_if(carg->sock < 0, RESOURCED_ERROR_FAIL,
1145                 "Failed to create and bind netlink socket.");
1146
1147         carg->family_id_stat = get_family_id(carg->sock,
1148                 carg->pid, "TRAF_STAT");
1149         if (carg->family_id_stat == 0) {
1150                 _E("Failed to get family id for TRAF_STAT.");
1151                 error = RESOURCED_ERROR_FAIL;
1152                 goto release_sock;
1153         }
1154
1155         carg->family_id_restriction = get_family_id(carg->sock,
1156                 carg->pid, "REST_NOTI");
1157
1158         if (carg->family_id_restriction ==  0) {
1159                 _E("Failed to get family id for REST_NOTI.");
1160                 error = RESOURCED_ERROR_FAIL;
1161                 goto release_sock;
1162         }
1163         /*thereafter we'll be able to receive message from server */
1164         send_start(carg->sock, carg->pid, carg->family_id_stat);
1165
1166         return RESOURCED_ERROR_NONE;
1167 release_sock:
1168         close(carg->sock);
1169         return error;
1170 }
1171 #endif /* CONFIG_DATAUSAGE_NFACCT */
1172
1173
1174 int resourced_init_counter_func(struct counter_arg *carg)
1175 {
1176         int error = 0;
1177
1178         if (!carg) {
1179                 _E("Please provide valid argument for counting routine.");
1180                 error = RESOURCED_ERROR_INVALID_PARAMETER;
1181                 return error;
1182         }
1183
1184         error = init_sock(carg);
1185         ret_value_msg_if(error != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL,
1186                          "Couldn't init socket!");
1187
1188         carg->result = create_app_stat_tree();
1189         carg->in_tree = create_traffic_stat_tree();
1190         carg->out_tree = create_traffic_stat_tree();
1191 #ifdef CONFIG_DATAUSAGE_NFACCT
1192         carg->nf_cntrs = create_nfacct_tree();
1193 #endif /* CONFIG_DATAUSAGE_NFACCT */
1194
1195         init_iftype();
1196
1197         error = edbus_add_methods(RESOURCED_PATH_NETWORK, edbus_methods,
1198                           ARRAY_SIZE(edbus_methods));
1199
1200         if (error != RESOURCED_ERROR_NONE)
1201                 _E("DBus method registration for %s is failed",
1202                         RESOURCED_PATH_NETWORK);
1203
1204         _counter_func_cb(carg);
1205
1206         carg->ecore_timer = ecore_timer_add(carg->opts->update_period,
1207                                            _counter_func_cb, carg);
1208
1209         ret_value_msg_if(carg->ecore_timer == 0, RESOURCED_ERROR_FAIL,
1210                          "carg_timer is null, can't work! update period: %d",
1211                          carg->opts->update_period);
1212
1213         carg->ecore_fd_handler = ecore_main_fd_handler_add(
1214                 carg->sock, ECORE_FD_READ, _answer_func_cb, carg, NULL, NULL);
1215         _D("ecore_carg_handler = %p", carg->ecore_fd_handler);
1216
1217         return error;
1218 }
1219
1220 static void finalize_quota_insert(void)
1221 {
1222         if (datausage_quota_insert) {
1223                 sqlite3_finalize(datausage_quota_insert);
1224                 datausage_quota_insert = NULL;
1225         }
1226 }
1227
1228 static void finalize_quota_remove(void)
1229 {
1230         if (datausage_quota_remove) {
1231                 sqlite3_finalize(datausage_quota_remove);
1232                 datausage_quota_remove = NULL;
1233         }
1234 }
1235
1236 void resourced_finalize_counter_func(struct counter_arg *carg)
1237 {
1238         ret_msg_if(carg == NULL, "Invalid counter argument\n");
1239         free_traffic_stat_tree(carg->out_tree);
1240         free_traffic_stat_tree(carg->in_tree);
1241         nulify_app_stat_tree(&carg->result);
1242         ecore_main_fd_handler_del(carg->ecore_fd_handler);
1243         ecore_timer_del(carg->ecore_timer);
1244         finalize_quota_insert();
1245         finalize_quota_remove();
1246 }