4 * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * @file counter-process.c
22 * @desc Counter process entity
24 * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
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"
43 #include "module-data.h"
44 #include "notification.h"
45 #include "resourced.h"
49 #include "transmission.h"
50 #include "datausage-vconf-common.h"
55 #include <linux/genetlink.h>
56 #include <linux/netlink.h>
57 #include <linux/netfilter/nfnetlink.h>
60 static char *null_str = "(null)";
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=? " \
70 #define QUOTA_CEILING_VALUE 10737418220
72 /* Warning threshold part in percent*/
74 WARNING_THRESHOLD_DEFAULT_PART = 5,
75 WARNING_THRESHOLD_PART_10 = 10,
76 WARNING_THRESHOLD_PART_15 = 15,
77 WARNING_THRESHOLD_PART_20 = 20,
80 static sqlite3_stmt *datausage_quota_insert;
81 static sqlite3_stmt *datausage_quota_remove;
83 static bool check_net_blocked(sig_atomic_t state)
85 static int net_blocked; /* counter for run only one time after blocking
87 if (state & RESOURCED_NET_BLOCKED_STATE &&
91 /* set net_blocked flag */
93 state & RESOURCED_NET_BLOCKED_STATE)
95 /* reset net_blocked flag */
97 !(state & RESOURCED_NET_BLOCKED_STATE))
99 _D("net_blocked %d, state %d", net_blocked, state);
103 #ifdef CONFIG_DATAUSAGE_NFACCT
104 static Eina_Bool send_counter_request(struct counter_arg *carg)
106 return nfacct_send_get(carg) == RESOURCED_ERROR_NONE ?
107 ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL;
110 /* TODO exclude wlll be broken */
111 static nfacct_rule_jump get_counter_jump(nfacct_rule_intend intend)
113 if (intend == NFACCT_WARN)
114 return NFACCT_JUMP_ACCEPT;
115 else if (intend == NFACCT_BLOCK)
116 return NFACCT_JUMP_REJECT;
118 return NFACCT_JUMP_UNKNOWN;
121 static void populate_counters(char *cnt_name,
122 struct counter_arg *carg)
124 struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
125 nfacct_rule_jump jump = NFACCT_JUMP_UNKNOWN;
127 if (!recreate_counter_by_name(cnt_name, &counter)) {
128 _E("Can't parse counter name %s", cnt_name);
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,
139 produce_net_rule(&counter, 0, 0,
140 NFACCT_ACTION_APPEND, jump, counter.iotype);
142 keep_counter(&counter);
145 static void populate_traf_stat_list(char *cnt_name, uint64_t bytes,
146 struct counter_arg *carg)
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;
153 _D("cnt_name %s", cnt_name);
155 if (!recreate_counter_by_name(cnt_name, &counter)) {
156 _E("Can't parse counter name %s", cnt_name);
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);
163 if (counter.iotype == NFACCT_COUNTER_UNKNOWN ||
164 counter.intend != NFACCT_COUNTER) {
165 _E("Counter type is not supported!");
169 tree = counter.iotype == NFACCT_COUNTER_IN ? carg->in_tree : carg->out_tree;
170 to_insert = g_new(struct traffic_stat, 1);
172 _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat));
176 key = g_new(struct classid_iftype_key, 1);
179 _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key));
180 g_free((gpointer)to_insert);
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);
192 static int fill_counters(struct rtattr *attr_list[__NFACCT_MAX],
195 struct counter_arg *carg = user_data;
196 char *cnt_name = (char *)RTA_DATA(
197 attr_list[NFACCT_NAME]);
199 populate_counters(cnt_name, carg);
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_*
207 populate_traf_stat_list(cnt_name, bytes, carg);
213 static int post_fill_counters(void *user_data)
215 struct counter_arg *carg = user_data;
224 static Eina_Bool send_counter_request(struct counter_arg *carg)
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");
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");
236 return ECORE_CALLBACK_RENEW;
238 #endif /* CONFIG_DATAUSAGE_NFACCT */
240 static Eina_Bool _counter_func_cb(void *user_data)
242 struct counter_arg *carg = (struct counter_arg *)user_data;
244 if (check_net_blocked(carg->opts->state)) {
245 ecore_timer_freeze(carg->ecore_timer);
246 return ECORE_CALLBACK_RENEW;
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
255 return send_counter_request(carg);
259 return ECORE_CALLBACK_CANCEL;
262 static dbus_bool_t deserialize_restriction(
263 DBusMessage *msg, char **appid, resourced_net_restrictions *rest,
264 enum traffic_restriction_type *rst_type)
267 dbus_error_init(&err);
269 int ret = dbus_message_get_args(
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),
283 _E("Can't deserialize quota! [%s:%s]\n",
284 err.name, err.message);
287 dbus_error_free(&err);
292 static DBusMessage *edbus_process_restriction(E_DBus_Object *obj,
295 DBusMessageIter iter;
298 resourced_ret_c dbus_ret = RESOURCED_ERROR_NONE;
300 resourced_net_restrictions rest;
301 enum traffic_restriction_type rst_type;
303 ret = dbus_message_is_method_call(
304 msg, RESOURCED_INTERFACE_NETWORK,
305 RESOURCED_NETWORK_PROCESS_RESTRICTION);
308 return dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD,
309 "Method is not supported");
311 ret = deserialize_restriction(msg, &appid, &rest, &rst_type);
313 reply = dbus_message_new_method_return(msg);
314 dbus_message_iter_init_append(reply, &iter);
316 dbus_ret = RESOURCED_ERROR_FAIL;
320 dbus_ret = proc_keep_restriction(appid, NONE_QUOTA_ID, &rest,
323 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &dbus_ret);
328 static DBusMessage *edbus_update_counters(E_DBus_Object *obj, DBusMessage *msg)
331 struct shared_modules_data *m_data = get_shared_modules_data();
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");
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;
343 /* postpone periodic update on one minute */
344 reschedule_count_timer(m_data->carg, COUNTER_UPDATE_PERIOD);
345 _counter_func_cb(m_data->carg);
348 reply = dbus_message_new_method_return(msg);
352 struct serialization_quota {
356 int snd_warning_threshold;
357 int rcv_warning_threshold;
358 resourced_state_t quota_type;
359 resourced_iface_type iftype;
361 resourced_roaming_type roaming_type;
364 static inline int _get_threshold_part(int time_period)
366 if (time_period < RESOURCED_PERIOD_DAY)
367 return WARNING_THRESHOLD_PART_20;
369 if (time_period < RESOURCED_PERIOD_WEEK)
370 return WARNING_THRESHOLD_PART_15;
372 if (time_period < RESOURCED_PERIOD_MONTH)
373 return WARNING_THRESHOLD_PART_10;
375 return WARNING_THRESHOLD_DEFAULT_PART;
378 static inline int64_t get_quota_ceiling(const int64_t quota)
380 return quota >= QUOTA_CEILING_VALUE ? QUOTA_CEILING_VALUE :
384 static inline int _evaluate_warning_threshold(const int64_t quota,
385 const int time_period, const int user_threshold)
387 int threshold_part = WARNING_THRESHOLD_DEFAULT_PART;
389 if (user_threshold != WARNING_THRESHOLD_DEFAULT)
390 return user_threshold;
392 threshold_part = _get_threshold_part(time_period);
394 return (get_quota_ceiling(quota) / 100 ) * threshold_part;
397 static dbus_bool_t deserialize_quota(
398 DBusMessage *msg, char **appid,
399 struct serialization_quota *quota)
402 dbus_error_init(&err);
404 int ret = dbus_message_get_args(
406 DBUS_TYPE_STRING, appid,
407 DBUS_TYPE_INT32, "a->time_period,
408 DBUS_TYPE_UINT64, "a->snd_quota,
409 DBUS_TYPE_UINT64, "a->rcv_quota,
410 DBUS_TYPE_INT32, "a->snd_warning_threshold,
411 DBUS_TYPE_INT32, "a->rcv_warning_threshold,
412 DBUS_TYPE_INT32, "a->quota_type,
413 DBUS_TYPE_INT32, "a->iftype,
414 DBUS_TYPE_INT32, "a->start_time,
415 DBUS_TYPE_INT32, "a->roaming_type,
418 _E("Can't deserialize set quota message ![%s:%s]\n",
419 err.name, err.message);
422 dbus_error_free(&err);
424 quota->iftype = (quota->iftype == RESOURCED_IFACE_UNKNOWN) ?
425 RESOURCED_IFACE_ALL : quota->iftype;
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);
437 static dbus_bool_t deserialize_remove_quota(
438 DBusMessage *msg, char **appid,
439 resourced_iface_type *iftype, resourced_roaming_type *roaming)
442 dbus_error_init(&err);
444 int ret = dbus_message_get_args(
446 DBUS_TYPE_STRING, appid,
447 DBUS_TYPE_INT32, iftype,
448 DBUS_TYPE_INT32, roaming,
451 _E("Can't deserialize remove quota message! [%s:%s]\n",
452 err.name, err.message);
455 dbus_error_free(&err);
460 static DBusMessage *edbus_join_net_stat(E_DBus_Object *obj, DBusMessage *msg)
464 resourced_ret_c ret = RESOURCED_ERROR_NONE;
466 DBusMessageIter iter;
469 dbus_error_init(&err);
471 if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
472 RESOURCED_NETWORK_JOIN_NET_STAT) == 0) {
473 ret = RESOURCED_ERROR_INVALID_PARAMETER;
477 ret = dbus_message_get_args(
479 DBUS_TYPE_STRING, &app_id,
480 DBUS_TYPE_INT32, &pid,
483 _E("Can't deserialize join netstat message! [%s:%s]\n",
484 err.name, err.message);
485 ret = RESOURCED_ERROR_INVALID_PARAMETER;
489 ret = join_net_cls(app_id, pid);
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);
496 dbus_error_free(&err);
500 static int init_datausage_quota_remove(sqlite3 *db)
504 if (datausage_quota_remove)
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);
519 static resourced_ret_c remove_quota(const char *app_id,
520 resourced_iface_type iftype, resourced_roaming_type roaming)
522 resourced_ret_c error_code = RESOURCED_ERROR_NONE;
523 libresourced_db_initialize_once();
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;
531 if (sqlite3_bind_text(datausage_quota_remove, 1, app_id, -1, SQLITE_STATIC) !=
533 _SE("Can not bind app_id: %s for preparing statement",
535 error_code = RESOURCED_ERROR_DB_FAILED;
539 if (sqlite3_bind_int(datausage_quota_remove, 2, iftype)
541 _E("Can not bind iftype:%d for preparing statement",
543 error_code = RESOURCED_ERROR_DB_FAILED;
547 if (sqlite3_bind_int(datausage_quota_remove, 3, roaming)
549 _E("Can not bind iftype:%d for preparing statement",
551 error_code = RESOURCED_ERROR_DB_FAILED;
555 if (sqlite3_step(datausage_quota_remove) != SQLITE_DONE) {
556 _E("failed to remove record");
557 error_code = RESOURCED_ERROR_DB_FAILED;
561 restriction_set_status(RESTRICTION_STATE_UNSET);
563 _SD("quota for app %s removed", app_id);
566 sqlite3_reset(datausage_quota_remove);
570 static DBusMessage *edbus_remove_quota(E_DBus_Object *obj, DBusMessage *msg)
573 resourced_iface_type iftype;
574 resourced_ret_c ret = RESOURCED_ERROR_NONE;
575 resourced_roaming_type roaming;
577 DBusMessageIter iter;
579 if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
580 RESOURCED_NETWORK_REMOVE_QUOTA) == 0) {
581 ret = RESOURCED_ERROR_INVALID_PARAMETER;
585 if (deserialize_remove_quota(msg, &app_id, &iftype, &roaming)
587 ret = RESOURCED_ERROR_INVALID_PARAMETER;
591 ret = remove_quota(app_id, iftype, roaming);
592 update_quota_state(app_id, iftype, 0, 0, roaming);
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);
601 static int init_datausage_quota_insert(sqlite3 *db)
605 if (datausage_quota_insert)
608 rc = sqlite3_prepare_v2(db, INSERT_QUERY,
609 -1, &datausage_quota_insert, NULL);
611 if (rc != SQLITE_OK) {
612 _E("can not prepare datausage_quota_insert");
613 datausage_quota_insert = NULL;
614 sqlite3_finalize(datausage_quota_insert);
620 static resourced_ret_c store_quota(const char *app_id,
621 const struct serialization_quota *quota)
623 resourced_ret_c error_code = RESOURCED_ERROR_NONE;
625 libresourced_db_initialize_once();
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;
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;
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",
645 error_code = RESOURCED_ERROR_DB_FAILED;
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",
653 error_code = RESOURCED_ERROR_DB_FAILED;
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;
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;
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",
677 error_code = RESOURCED_ERROR_DB_FAILED;
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",
685 error_code = RESOURCED_ERROR_DB_FAILED;
689 if (sqlite3_bind_int(datausage_quota_insert, 8,
690 quota->iftype) != SQLITE_OK) {
691 _E("Can not bind iftype: %d for preparing statement",
693 error_code = RESOURCED_ERROR_DB_FAILED;
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",
701 error_code = RESOURCED_ERROR_DB_FAILED;
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;
712 sqlite3_reset(datausage_quota_insert);
716 static DBusMessage *edbus_create_quota(E_DBus_Object *obj, DBusMessage *msg)
719 DBusMessageIter iter;
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;
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;
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;
743 deserialize_quota(msg, &app_id, "a);
744 ret = store_quota(app_id, "a);
745 if (ret != RESOURCED_ERROR_NONE) {
746 _E("Can't store quota!");
750 update_quota_state(app_id, quota.iftype, quota.start_time,
751 quota.time_period, quota.roaming_type);
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!");
758 carg->opts->is_update_quota = 1;
759 reschedule_count_timer(carg, 0);
760 _SD("Datausage quota changed");
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);
769 struct get_stats_context {
774 DBusMessageIter iter;
777 static resourced_cb_ret answer_get_stat(const data_usage_info *info,
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));
783 ret_value_msg_if(insert == NULL, RESOURCED_CANCEL, "Can't allocate memory!");
784 memcpy(insert, info, sizeof(data_usage_info));
786 int app_id_len = strlen(info->app_id) + 1;
787 insert->app_id = (char *)malloc(app_id_len);
788 if (!insert->app_id) {
790 _E("Malloc of answer_get_stat failed\n");
791 return RESOURCED_CANCEL;
794 strncpy((char *)insert->app_id, info->app_id, app_id_len);
796 ctx->infos = g_slist_append(ctx->infos, insert);
797 return RESOURCED_CONTINUE;
800 static void prepare_response(struct get_stats_context *ctx)
803 data_usage_info *info;
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);
810 gslist_for_each_item(iter, ctx->infos) {
811 info = (data_usage_info *)iter->data;
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,
820 dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
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);
826 dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.incoming_bytes);
828 dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.outgoing_bytes);
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);
833 dbus_message_iter_close_container(&arr, &sub);
836 dbus_message_iter_close_container(&ctx->iter, &arr);
837 g_slist_free_full(ctx->infos, free);
840 static void deserialize_rule(DBusMessage *msg, data_usage_selection_rule *rule, char **app_id)
843 dbus_error_init(&err);
845 int ret = dbus_message_get_args(
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,
855 _E("Can't deserialize quota! [%s:%s]\n",
856 err.name, err.message);
859 if (app_id && !strcmp(*app_id, null_str))
861 dbus_error_free(&err);
864 static DBusMessage *edbus_get_stats(E_DBus_Object *obj, DBusMessage *msg)
866 data_usage_selection_rule rule;
869 struct get_stats_context ctx;
872 if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
873 RESOURCED_NETWORK_GET_STATS) == 0) {
874 ret = RESOURCED_ERROR_INVALID_PARAMETER;
878 _SD("Datausage get stats");
880 deserialize_rule(msg, &rule, &app_id);
882 ret = data_usage_details_foreach(app_id, &rule, answer_get_stat,
885 ret = data_usage_foreach(&rule, answer_get_stat, &ctx);
887 prepare_response(&ctx);
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);
897 struct nl_family_params {
899 struct counter_arg *carg;
903 struct nl_family_params params;
904 void (*process)(struct nl_family_params *params);
905 } nl_serialization_command;
907 static inline char *_get_public_appid(const uint32_t classid)
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;
916 appid = get_app_id_by_classid(classid, true);
917 return !appid ? UNKNOWN_APP : appid;
920 static bool need_flush_immediatelly(sig_atomic_t state)
922 return state & RESOURCED_FORCIBLY_FLUSH_STATE ||
923 state & RESOURCED_FORCIBLY_QUIT_STATE;
926 static Eina_Bool _store_and_free_result_cb(void *user_data)
928 struct counter_arg *arg = (struct counter_arg *)user_data;
930 ret_value_msg_if(!arg, ECORE_CALLBACK_CANCEL, "Please provide valid argument!");
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");
949 arg->store_result_timer = NULL;
950 return ECORE_CALLBACK_CANCEL;
953 static void _store_and_free_result(struct counter_arg *arg)
955 if (!arg->store_result_timer)
956 arg->store_result_timer = ecore_timer_add(STORE_DELAY_INTERVAL,
957 _store_and_free_result_cb, arg);
960 static void _process_network_counter(struct nl_family_params *params)
963 struct netlink_serialization_params ser_params = {
964 .carg = params->carg,
966 #ifdef CONFIG_DATAUSAGE_NFACCT
967 .eval_attr = fill_counters,
968 .post_eval_attr = post_fill_counters,
972 netlink_serialization_command *netlink =
973 netlink_create_command(&ser_params);
976 _E("Can not create command");
980 netlink->deserialize_answer(&(netlink->params));
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)
988 pthread_rwlock_wrlock(¶ms->carg->result->guard);
989 ret = prepare_application_stat(params->carg->in_tree,
990 params->carg->out_tree, params->carg->result,
992 pthread_rwlock_unlock(¶ms->carg->result->guard);
994 if (ret != RESOURCED_ERROR_NONE) {
995 _E("Failed to prepare application statistics!");
998 ret = process_quota(params->carg->result, params->carg->opts);
1000 _E("Failed to process quota!");
1004 _store_and_free_result(params->carg);
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);
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)
1016 command->process = _process_network_counter;
1017 return RESOURCED_ERROR_NONE;
1021 static void _process_restriction(struct nl_family_params *cmd)
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;
1030 _D("Restriction notification");
1032 if (process_netlink_restriction_msg(cmd->ans, &restriction,
1033 ¬ification_type) !=
1034 RESOURCED_ERROR_NONE) {
1035 _E("Failed to process netlink restriction.");
1039 app_id = _get_public_appid(restriction.sk_classid);
1040 iftype = get_iftype(restriction.ifindex);
1042 ret = get_restriction_info(app_id, iftype, &rst_info);
1043 ret_msg_if(ret != RESOURCED_ERROR_NONE,
1044 "Failed to get restriction info!");
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);
1057 _E("Unkown restriction notification type");
1060 static resourced_ret_c choose_netlink_process(struct genl *ans,
1061 nl_serialization_command *command, struct counter_arg *carg)
1063 int family = netlink_get_family(ans);
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;
1070 _E("General netlink family %d unsupported!", family);
1071 return RESOURCED_ERROR_NO_DATA;
1073 return RESOURCED_ERROR_NONE;
1075 #endif /* CONFIG_DATAUSAGE_NFACCT */
1077 static nl_serialization_command *choose_handler(struct genl *ans,
1078 struct counter_arg *carg)
1080 static nl_serialization_command command;
1081 resourced_ret_c ret;
1083 if (!ans || !carg) {
1084 _E("Please provide valid pointer!");
1088 if (!command.params.carg)
1089 command.params.carg = carg;
1090 command.params.ans = ans;
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");
1099 static Eina_Bool _answer_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
1101 struct counter_arg *carg = (struct counter_arg *)user_data;
1103 nl_serialization_command *netlink_handler = NULL;
1106 ret = read_netlink(carg->sock, &ans, sizeof(struct genl));
1109 carg->ans_len = ret;
1110 netlink_handler = choose_handler(&ans, carg);
1112 if (!netlink_handler)
1115 netlink_handler->process(&(netlink_handler->params));
1118 return ECORE_CALLBACK_RENEW;
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 },
1131 #ifdef CONFIG_DATAUSAGE_NFACCT
1132 int init_sock(struct counter_arg *carg)
1134 carg->sock = create_netlink(NETLINK_NETFILTER, 0);
1135 return carg->sock != 0 ? RESOURCED_ERROR_NONE :
1136 RESOURCED_ERROR_FAIL;
1139 int init_sock(struct counter_arg *carg)
1141 int error = RESOURCED_ERROR_NONE;
1142 carg->sock = create_netlink(NETLINK_GENERIC, 0);
1144 ret_value_msg_if(carg->sock < 0, RESOURCED_ERROR_FAIL,
1145 "Failed to create and bind netlink socket.");
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;
1155 carg->family_id_restriction = get_family_id(carg->sock,
1156 carg->pid, "REST_NOTI");
1158 if (carg->family_id_restriction == 0) {
1159 _E("Failed to get family id for REST_NOTI.");
1160 error = RESOURCED_ERROR_FAIL;
1163 /*thereafter we'll be able to receive message from server */
1164 send_start(carg->sock, carg->pid, carg->family_id_stat);
1166 return RESOURCED_ERROR_NONE;
1171 #endif /* CONFIG_DATAUSAGE_NFACCT */
1174 int resourced_init_counter_func(struct counter_arg *carg)
1179 _E("Please provide valid argument for counting routine.");
1180 error = RESOURCED_ERROR_INVALID_PARAMETER;
1184 error = init_sock(carg);
1185 ret_value_msg_if(error != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL,
1186 "Couldn't init socket!");
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 */
1197 error = edbus_add_methods(RESOURCED_PATH_NETWORK, edbus_methods,
1198 ARRAY_SIZE(edbus_methods));
1200 if (error != RESOURCED_ERROR_NONE)
1201 _E("DBus method registration for %s is failed",
1202 RESOURCED_PATH_NETWORK);
1204 _counter_func_cb(carg);
1206 carg->ecore_timer = ecore_timer_add(carg->opts->update_period,
1207 _counter_func_cb, carg);
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);
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);
1220 static void finalize_quota_insert(void)
1222 if (datausage_quota_insert) {
1223 sqlite3_finalize(datausage_quota_insert);
1224 datausage_quota_insert = NULL;
1228 static void finalize_quota_remove(void)
1230 if (datausage_quota_remove) {
1231 sqlite3_finalize(datausage_quota_remove);
1232 datausage_quota_remove = NULL;
1236 void resourced_finalize_counter_func(struct counter_arg *carg)
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();