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.
21 * @file datausage-quota-processing.c
23 * @desc Quota processing implementation.
24 * This implementation updates used quota table and determine
25 * moment of time for blocking.
27 * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
38 #include "data_usage.h"
40 #include "protocol-info.h"
41 #include "resourced.h"
42 #include "notification.h"
46 #include "datausage-restriction.h"
47 #include "datausage-vconf-common.h"
50 static sqlite3_stmt *select_stmt;
51 static sqlite3_stmt *insert_stmt;
52 static sqlite3_stmt *clear_effective_stmt;
54 static const char select_query[] = "SELECT qt.binpath, qt.sent_quota, qt.rcv_quota, "\
55 "qt.snd_warning_threshold, qt.rcv_warning_threshold, "\
56 "sent_used_quota, rcv_used_quota, qt.start_time AS quota_start_time, "\
57 "qt.time_period AS quota_period, efq.start_time AS effective_start, "\
58 "efq.finish_time AS effective_finish, qt.iftype AS iftype, " \
63 "LEFT OUTER JOIN effective_quotas AS efq ON (qt.binpath = efq.binpath "\
64 "AND qt.iftype = efq.iftype AND qt.roaming = efq.roaming) "\
65 "GROUP BY qt.binpath, qt.iftype, qt.sent_quota, qt.rcv_quota, " \
68 static const char insert_query[] = "REPLACE INTO effective_quotas " \
69 "(binpath, sent_used_quota, rcv_used_quota, " \
70 "start_time, finish_time, iftype, roaming, state) " \
71 " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
73 static const char clear_effective_quota_query[] = "DELETE FROM effective_quotas " \
74 " WHERE binpath = ? AND iftype = ? AND roaming = ?";
76 enum resourced_quota_state {
77 RESOURCED_QUOTA_UNKNOWN, /**< undefined/initial state */
78 RESOURCED_QUOTA_APPLIED, /**< enabled/applied state */
79 RESOURCED_QUOTA_REVERTED, /**< disabled/reverted state */
86 int64_t sent_used_quota;
87 int64_t rcv_used_quota;
88 int snd_warning_threshold;
89 int rcv_warning_threshold;
94 enum resourced_quota_state state;
99 resourced_iface_type iftype;
100 resourced_roaming_type roaming;
109 static void obtain_and_keep_quotas(sqlite3_stmt *query)
112 struct quota *value = 0;
113 struct quota_key *key = 0;
116 _D("Can not update quotas: empty query");
121 rc = sqlite3_step(query);
123 if (rc == SQLITE_ERROR) {
124 _E("Error updating quotas %s", sqlite3_errmsg(resourced_get_database()));
126 } else if (rc == SQLITE_ROW) {
127 value = g_new0(struct quota, 1);
129 _E("Can't allocate value for quota");
133 key = g_new0(struct quota_key, 1);
135 _E("Can't allocate key for quota");
139 key->app_id = strdup((char *)sqlite3_column_text(
141 key->iftype = sqlite3_column_int(
143 key->roaming = sqlite3_column_int(
146 value->send_quota = sqlite3_column_int64(
148 value->rcv_quota = sqlite3_column_int64(
150 value->snd_warning_threshold = sqlite3_column_int64(
152 value->rcv_warning_threshold = sqlite3_column_int64(
154 value->sent_used_quota = sqlite3_column_int64(
156 value->rcv_used_quota = sqlite3_column_int64(
158 value->start_time = sqlite3_column_int64(
160 value->time_period = sqlite3_column_int64(
162 value->real_start = sqlite3_column_int64(
164 value->real_finish = sqlite3_column_int64(
166 value->state = sqlite3_column_int64(
168 value->quota_id = sqlite3_column_int(
171 g_tree_insert(quotas, key, value);
173 } while (rc == SQLITE_ROW);
181 static gint compare_quota_key(gconstpointer a, gconstpointer b,
182 gpointer UNUSED user_data)
184 const struct quota_key *key1 = a;
185 const struct quota_key *key2 = b;
186 /* the first part of the key is equal compare second */
187 return strcmp(key1->app_id, key2->app_id) ||
188 key1->iftype - key2->iftype ||
189 key1->roaming - key2->roaming;
192 #define quota_key_destructor g_free
193 #define quota_destructor g_free
195 static void _clear_effective_quota(const char *app_id,
196 const resourced_iface_type iftype,
197 const resourced_roaming_type roaming)
199 if (sqlite3_bind_text(clear_effective_stmt, 1, app_id, -1,
200 SQLITE_TRANSIENT) != SQLITE_OK) {
201 _SE("Can not bind app_id:%s for preparing statement:%s",
202 app_id, sqlite3_errmsg(resourced_get_database()));
206 if (sqlite3_bind_int(clear_effective_stmt, 2, iftype)
208 _E("Can not bind iftype:%d for preparing statement:%s",
209 iftype, sqlite3_errmsg(resourced_get_database()));
213 if (sqlite3_bind_int(clear_effective_stmt, 3, roaming)
215 _E("Can not bind roaming:%d for preparing statement:%s",
216 roaming, sqlite3_errmsg(resourced_get_database()));
220 if (sqlite3_step(clear_effective_stmt) != SQLITE_DONE)
221 _E("Failed to clear effective quotas %s",
222 sqlite3_errmsg(resourced_get_database()));
223 sqlite3_reset(clear_effective_stmt);
226 static inline int _is_period_devisible(const int time_period,
227 data_usage_quota_period_t quota_period)
229 return time_period > quota_period &&
230 time_period % RESOURCED_PERIOD_MONTH == 0;
234 * @desc Define period base on stored in data base time interval
235 * @return time period
237 static data_usage_quota_period_t _define_period(const int time_period, int *quantity)
240 return RESOURCED_PERIOD_UNDEF;
242 if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
243 *quantity = time_period / RESOURCED_PERIOD_MONTH;
244 return RESOURCED_PERIOD_MONTH;
247 if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
248 *quantity = time_period / RESOURCED_PERIOD_MONTH;
249 return RESOURCED_PERIOD_MONTH;
252 if (_is_period_devisible(time_period, RESOURCED_PERIOD_WEEK)) {
253 *quantity = time_period / RESOURCED_PERIOD_WEEK;
254 return RESOURCED_PERIOD_WEEK;
257 if (_is_period_devisible(time_period, RESOURCED_PERIOD_DAY)) {
258 *quantity = time_period / RESOURCED_PERIOD_DAY;
259 return RESOURCED_PERIOD_DAY;
262 if (_is_period_devisible(time_period, RESOURCED_PERIOD_HOUR)) {
263 *quantity = time_period / RESOURCED_PERIOD_HOUR;
264 return RESOURCED_PERIOD_HOUR;
267 *quantity = time_period;
268 return RESOURCED_PERIOD_UNDEF;
272 static time_t _get_finish_time(const time_t start_time, const int time_period)
277 if (gmtime_r((const time_t *)&start_time, &new_start) == NULL)
280 switch (_define_period(time_period, &quantity)) {
281 case RESOURCED_PERIOD_UNDEF:
282 return start_time + time_period;
283 case RESOURCED_PERIOD_HOUR:
284 new_start.tm_hour += quantity;
286 case RESOURCED_PERIOD_DAY:
287 new_start.tm_mday += quantity;
289 case RESOURCED_PERIOD_WEEK:
290 new_start.tm_mday += quantity * 7;
292 case RESOURCED_PERIOD_MONTH:
293 new_start.tm_mon += quantity;
298 return mktime(&new_start);
301 struct data_usage_context {
302 int64_t sent_used_quota;
303 int64_t rcv_used_quota;
304 resourced_roaming_type roaming;
307 static resourced_cb_ret data_usage_details_cb(const data_usage_info *info,
310 struct data_usage_context *context =
311 (struct data_usage_context *)user_data;
314 (context->roaming != RESOURCED_ROAMING_UNKNOWN &&
315 context->roaming != info->roaming))
316 return RESOURCED_CONTINUE;
318 context->sent_used_quota = info->foreground.cnt.incoming_bytes;
319 context->rcv_used_quota = info->foreground.cnt.outgoing_bytes;
320 return RESOURCED_CANCEL; /* only one entry allowed */
323 static void _record_quota(const struct quota_key *key,
324 const struct quota *app_quota)
326 if (!key || !app_quota) {
327 _E("Please, provide valid argument.");
331 if (!app_quota->sent_used_quota &&
332 !app_quota->rcv_used_quota) {
333 _D("Nothing to store for effective quota.");
337 if (sqlite3_bind_text(insert_stmt, 1, key->app_id, -1,
338 SQLITE_STATIC) != SQLITE_OK) {
339 _SE("Can not bind app_id:%s for preparing statement",
344 if (sqlite3_bind_int64(insert_stmt, 2, app_quota->sent_used_quota)
346 _E("Can not bind sent_used_quota:%lld for preparing statement",
347 app_quota->sent_used_quota);
351 if (sqlite3_bind_int64(insert_stmt, 3, app_quota->rcv_used_quota)
353 _E("Can not bind rcv_used_quota:%lld for preparing statement",
354 app_quota->rcv_used_quota);
358 if (sqlite3_bind_int64(insert_stmt, 4, app_quota->real_start)
360 _E("Can not bind start_time:%d for preparing statement",
361 app_quota->real_start);
365 if (sqlite3_bind_int64(insert_stmt, 5, app_quota->real_finish)
367 _E("Can not bind finish_time:%d for preparing statement",
368 app_quota->real_finish);
372 if (sqlite3_bind_int(insert_stmt, 6, key->iftype)
374 _E("Can not bind iftype:%d for preparing statement",
379 if (sqlite3_bind_int(insert_stmt, 7, key->roaming)
381 _E("Can not bind roaming:%d for preparing statement",
386 if (sqlite3_bind_int(insert_stmt, 8, app_quota->state)
388 _E("Can not bind state:%d for preparing statement",
393 if (sqlite3_step(insert_stmt) != SQLITE_DONE)
394 _D("Failed to record quotas %s", sqlite3_errmsg(resourced_get_database()));
395 sqlite3_reset(insert_stmt);
398 static void _set_effective_quota(const char *app_id,
399 const resourced_iface_type iftype, const time_t start_time,
400 const int time_period,
401 const resourced_roaming_type roaming)
403 data_usage_selection_rule rule = {0,};
404 struct data_usage_context out_context = {0,};
405 struct quota_key key_quota = {
410 struct quota app_quota = {0,};
411 const time_t cur_time = time(0);
414 if (cur_time < start_time) {
415 _D("No need to update effective quota!");
419 out_context.roaming = roaming;
420 rule.from = start_time;
422 rule.iftype = iftype;
424 if (data_usage_details_foreach(app_id, &rule, data_usage_details_cb,
425 &out_context) != RESOURCED_ERROR_NONE) {
426 _E("Cant obtain sent_used_quota/rcv_used_quota");
430 if (ctime_r(&start_time, buf) == NULL) {
431 _E("Input parameter start_time is invalid");
435 _SD("Get counted traffic for appid:%s, per"
436 "%s, incoming:%d, outgoing:%d", app_id, buf,
437 out_context.rcv_used_quota, out_context.sent_used_quota);
439 app_quota.sent_used_quota = out_context.sent_used_quota;
440 app_quota.rcv_used_quota = out_context.rcv_used_quota;
441 app_quota.real_start = start_time;
442 app_quota.real_finish = _get_finish_time(start_time, time_period);
443 app_quota.state = RESOURCED_QUOTA_APPLIED;
444 _record_quota(&key_quota, &app_quota);
447 void update_quota_state(const char *app_id,
448 const resourced_iface_type iftype,
449 const time_t start_time,
450 const int time_period,
451 const resourced_roaming_type roaming)
453 struct quota_key key;
454 struct quota *tree_value;
457 _SE("app_id must be not NULL");
463 key.roaming = roaming;
464 tree_value = (struct quota *)g_tree_search(quotas,
465 (GCompareFunc)compare_quota_key, &key);
467 if (tree_value && tree_value->state == RESOURCED_QUOTA_APPLIED) {
468 _SD("Removing quota and restriction for %s,%d", app_id, iftype);
469 /* Restrictions can't be separated */
470 remove_restriction_local(app_id, iftype);
471 g_tree_remove(quotas, (gconstpointer*)(&key));
472 _clear_effective_quota(app_id, iftype, roaming);
474 if (start_time && time_period)
475 _set_effective_quota(app_id, iftype, start_time,
476 time_period, roaming);
478 _SD("There is no quota %s,%d in tree", app_id, iftype);
481 static resourced_ret_c _init_quotas(void)
484 quotas = g_tree_new_full(compare_quota_key, NULL,
485 quota_key_destructor, quota_destructor);
488 if (!resourced_get_database())
489 return RESOURCED_ERROR_DB_FAILED;
491 if (select_stmt && insert_stmt)
492 return RESOURCED_ERROR_NONE;
494 if (sqlite3_prepare_v2(resourced_get_database(),
495 select_query, -1, &select_stmt,
496 NULL) != SQLITE_OK) {
497 _E("Error preparing query: %s, \
498 %s\n", select_query, sqlite3_errmsg(resourced_get_database()));
502 if (sqlite3_prepare_v2(resourced_get_database(),
503 insert_query, -1, &insert_stmt,
504 NULL) != SQLITE_OK) {
505 _E("Error preparing query: %s, \
506 %s\n", insert_query, sqlite3_errmsg(resourced_get_database()));
510 if (sqlite3_prepare_v2(resourced_get_database(),
511 clear_effective_quota_query,
512 -1, &clear_effective_stmt,
513 NULL) != SQLITE_OK) {
514 _E("Error preparing query: %s, \
515 %s\n", clear_effective_quota_query,
516 sqlite3_errmsg(resourced_get_database()));
520 return RESOURCED_ERROR_NONE;
522 /* Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op */
523 sqlite3_finalize(select_stmt);
524 sqlite3_finalize(insert_stmt);
525 sqlite3_finalize(clear_effective_stmt);
526 return RESOURCED_ERROR_DB_FAILED;
531 * Update quotas tree, where app_id will the key
533 static resourced_ret_c _update_quotas(void)
535 const resourced_ret_c ret = _init_quotas();
536 if (ret != RESOURCED_ERROR_NONE) {
537 _E("Failed to init quotas");
541 obtain_and_keep_quotas(select_stmt);
542 return RESOURCED_ERROR_NONE;
545 static const int64_t quota_gap_value[RESOURCED_IFACE_ALL] = {
546 5000, /* ~4.5MB UNKNOWN */
547 5000, /* ~3MB RESOURCED_IFACE_DATACALL */
548 6000000, /* ~6MB RESOURCED_IFACE_WIFI */
549 5000000, /* ~100MB RESOURCED_IFACE_WIRED */
550 6000000, /* ~6MB RESOURCED_IFACE_BLUETOOTH */
553 static const int64_t quota_datacall_gap_value[RESOURCED_PROTOCOL_MAX_ELEM] = {
554 5000, /* RESOURCED_PROTOCOL_NONE */
555 5000, /* RESOURCED_PROTOCOL_DATACALL_NOSVC */
556 5000, /* RESOURCED_PROTOCOL_DATACALL_EMERGENCY */
557 5000, /* RESOURCED_PROTOCOL_DATACALL_SEARCH */
558 5000, /* RESOURCED_PROTOCOL_DATACALL_2G */
559 5000, /* RESOURCED_PROTOCOL_DATACALL_2_5G #GPRS 40 kbit/s in practice */
560 18750, /* RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE 150 kbit/s in practice */
561 400000, /* RESOURCED_PROTOCOL_DATACALL_3G, 7Mb/s on QC device */
562 475000, /* RESOURCED_PROTOCOL_DATACALL_HSDPA */
563 5000000,/* RESOURCED_PROTOCOL_DATACALL_LTE */
567 * @desc this function returns valud per second
569 static int64_t _get_quota_gap(const resourced_iface_type iftype)
572 const resourced_hw_net_protocol_type proto = get_hw_net_protocol_type(iftype);
574 if (proto != RESOURCED_PROTOCOL_NONE)
575 return quota_datacall_gap_value[proto];
577 if (iftype > RESOURCED_IFACE_UNKNOWN &&
578 iftype < RESOURCED_IFACE_ALL)
579 return quota_gap_value[iftype];
581 return quota_gap_value[RESOURCED_IFACE_UNKNOWN];
584 int _is_under_restriction(const int64_t send_delta,
585 const int64_t rcv_delta,
586 const resourced_iface_type iftype,
589 /* multiply on 2, due */
590 const int64_t quota_gap = _get_quota_gap(iftype) * update_period;
592 _D("send_delta %"PRId64" rcv_delta%"PRId64" quota_gap %"PRId64""
594 send_delta, rcv_delta, quota_gap, update_period);
595 return send_delta <= quota_gap ||
596 rcv_delta <= quota_gap;
599 inline void _check_warning_threshold(const int64_t send_delta, const int64_t rcv_delta,
600 struct quota *app_quota, const char *appid)
602 ret_msg_if(!app_quota, "Please provide valid pointer");
604 if (send_delta <= app_quota->snd_warning_threshold ||
605 rcv_delta <= app_quota->rcv_warning_threshold) {
606 app_quota->snd_warning_threshold = 0;
607 app_quota->rcv_warning_threshold = 0;
608 send_restriction_warn_notification(appid);
612 inline static int _get_warning_limit(int64_t limit, int threshold)
614 if (limit < threshold) {
615 _E("Warning threshold is greater than limit!");
616 return WARNING_THRESHOLD_DEFAULT; /* 0 means kernel will
619 return limit - threshold;
622 static int cast_restriction_limit(int64_t delta)
631 static gboolean check_and_apply_node(gpointer key,
632 gpointer value, gpointer user_data)
634 struct quota *app_quota = value;
635 struct quota_key *key_quota = key;
636 int64_t send_delta, rcv_delta;
637 struct daemon_opts *opts = (struct daemon_opts *)user_data;
638 resourced_net_restrictions rst = { RESOURCED_STATE_UNKNOWN,
639 RESOURCED_IFACE_UNKNOWN };
641 /* do not check already applied quota*/
642 if (app_quota->state == RESOURCED_QUOTA_APPLIED)
645 send_delta = app_quota->send_quota - app_quota->sent_used_quota;
646 rcv_delta = app_quota->rcv_quota - app_quota->rcv_used_quota;
648 if (app_quota->send_quota <= 0 || app_quota->rcv_quota <= 0)
649 send_restriction_notification(key_quota->app_id);
651 _check_warning_threshold(send_delta, rcv_delta, app_quota,
654 if (_is_under_restriction(send_delta, rcv_delta, key_quota->iftype,
655 opts->update_period) &&
656 (key_quota->roaming == RESOURCED_ROAMING_UNKNOWN ||
657 key_quota->roaming == get_roaming())) {
658 if (!strcmp(key_quota->app_id, TETHERING_APP_NAME) &&
659 (send_delta > 0 || rcv_delta > 0))
660 /* in the case of tethering we send
661 restriction only that must apply now */
664 rst.send_limit = cast_restriction_limit(send_delta);
665 rst.rcv_limit = cast_restriction_limit(rcv_delta);
666 rst.snd_warning_limit = _get_warning_limit(
667 rst.send_limit, app_quota->snd_warning_threshold);
668 rst.rcv_warning_limit = _get_warning_limit(
669 rst.rcv_limit, app_quota->rcv_warning_threshold);
671 _SD("Applying quota for %s, iftype %d", key_quota->app_id,
673 rst.iftype = key_quota->iftype;
675 if (proc_keep_restriction(key_quota->app_id,
676 app_quota->quota_id, &rst,
677 RST_SET) == RESOURCED_ERROR_NONE) {
678 app_quota->state = RESOURCED_QUOTA_APPLIED;
679 _D("Restriction was applied successfully.");
683 return FALSE; /* continue iteration */
686 static void check_and_apply_quota(volatile struct daemon_opts *opts)
688 g_tree_foreach(quotas, check_and_apply_node, (void *)opts);
691 struct update_all_arg
693 resourced_iface_type iftype;
694 struct application_stat *app_stat;
697 static gboolean update_pseudo_app_entry(gpointer key,
698 gpointer value, gpointer user_data)
700 struct update_all_arg *arg = (struct
701 update_all_arg *)user_data;
702 const struct quota_key *qkey = (const struct
705 /* handle case for network interfaces*/
706 if ((!strcmp(qkey->app_id, RESOURCED_ALL_APP) &&
707 (qkey->iftype == RESOURCED_IFACE_UNKNOWN ||
708 qkey->iftype == RESOURCED_IFACE_ALL ||
709 qkey->iftype == arg->iftype) &&
710 (qkey->roaming == RESOURCED_ROAMING_UNKNOWN ||
711 qkey->roaming == arg->app_stat->is_roaming)) ||
712 !strcmp(qkey->app_id, TETHERING_APP_NAME)) {
713 struct quota *total_quota = (struct quota *)value;
715 total_quota->sent_used_quota += arg->app_stat->delta_snd;
716 total_quota->rcv_used_quota += arg->app_stat->delta_rcv;
717 arg->app_stat->delta_snd = 0;
718 arg->app_stat->delta_rcv = 0;
719 _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d ifindex %d\n",
720 total_quota->sent_used_quota, total_quota->rcv_used_quota,
721 arg->iftype, arg->app_stat->ifindex);
728 static void update_all_app_quotas(struct update_all_arg *update_all_arg)
730 /* Now RESOURCED_ALL_APP can contain many iftypes */
731 g_tree_foreach(quotas, update_pseudo_app_entry, update_all_arg);
734 static void update_traffic_quota(const struct quota_key *quota_key,
738 struct quota *found_quota = g_tree_lookup(quotas, quota_key);
742 if (time(0) < found_quota->start_time) {
743 _D("No need to update effective quota!");
746 found_quota->sent_used_quota += *snd_count;
747 found_quota->rcv_used_quota += *rcv_count;
748 _D("update total_quota tx:%"PRId64";rx:%"PRId64"\n",
749 found_quota->sent_used_quota, found_quota->rcv_used_quota);
751 _D("delta_rcv %d app_id %s\n", *rcv_count, quota_key->app_id);
757 static gboolean update_each_quota(gpointer key, gpointer value,
758 gpointer UNUSED userdata)
760 const struct classid_iftype_key *app_key =
761 (const struct classid_iftype_key *)key;
762 struct application_stat *app_stat =
763 (struct application_stat *)value;
764 struct update_all_arg arg = {
765 .iftype = app_key->iftype,
768 struct quota_key qkey;
770 /* We should handle cases of RESOURCED_ALL_APP or TETHERING_APP_NAME
771 in separate way due it's not comming with statistics from kernel */
772 update_all_app_quotas(&arg);
774 if (!app_stat->application_id)
777 qkey.app_id = app_stat->application_id;
778 qkey.iftype = app_key->iftype;
779 qkey.roaming = app_stat->is_roaming;
780 update_traffic_quota(&qkey, &app_stat->delta_snd,
781 &app_stat->delta_rcv);
785 static void actualize_quota_table(struct application_stat_tree *apps)
787 g_tree_foreach((GTree *)apps->tree, update_each_quota, NULL);
791 * @desc Assume app_quota is not null
793 static void calculate_finish_time(struct quota *app_quota)
795 if (!app_quota || app_quota->real_finish)
798 if (!app_quota->real_start)
799 app_quota->real_start = time(0);
801 app_quota->real_finish = _get_finish_time(app_quota->real_start,
802 app_quota->time_period);
806 * @desc Reset quota. This function sets new real_start based on fihish time.
807 * Assume app_quota is set and time(0) < app_quota->real_finish
809 static void reset_quota(struct quota *app_quota)
811 _D("reset_quota called");
812 app_quota->real_start = app_quota->real_finish;
813 app_quota->real_finish = 0;
814 app_quota->sent_used_quota = app_quota->rcv_used_quota = 0;
815 restriction_set_status(RESTRICTION_STATE_UNSET);
819 * @desc Remove restriction if needed
821 static void drop_restriction(const struct quota_key *qkey, struct quota *app_quota)
823 if (!app_quota || !qkey) {
824 _E("Please provide valid arguments!");
828 /* We can revert only applied quotas */
829 if (app_quota->state != RESOURCED_QUOTA_APPLIED)
832 _SD("Removing restriction of quota for %s,%d", qkey->app_id,
834 if (remove_restriction_local(qkey->app_id, qkey->iftype)
835 == RESOURCED_ERROR_NONE)
836 app_quota->state = RESOURCED_QUOTA_REVERTED;
840 * @desc This function actualize current quotas states. It calculate new
841 * finish time and remove restriction if exists.
843 static gboolean flush_quota_node(gpointer key,
844 gpointer value, gpointer UNUSED user_data)
846 struct quota *app_quota = value;
847 struct quota_key *key_quota = key;
849 if (!app_quota || !key_quota->app_id)
850 return FALSE; /* continue iteration even
851 current data is empty */
853 calculate_finish_time(app_quota);
855 _record_quota(key_quota, app_quota);
856 /* It's time to reset */
857 if (time(0) >= app_quota->real_finish) {
858 drop_restriction(key_quota, app_quota);
859 reset_quota(app_quota);
865 * Save to database effective quota
867 void flush_quota_table(void)
869 g_tree_foreach(quotas, flush_quota_node, NULL);
872 static void finalize_statement(sqlite3_stmt **stmt)
875 sqlite3_finalize(*stmt);
880 resourced_ret_c process_quota(struct application_stat_tree *apps,
881 volatile struct daemon_opts *opts)
883 /* For first initialization */
884 static int quota_updated;
886 if (opts && opts->is_update_quota) {
887 const int error = _update_quotas();
893 actualize_quota_table(apps);
895 check_and_apply_quota(opts);
898 if (opts && opts->is_update_quota && quota_updated) {
899 opts->is_update_quota = 0;
902 return RESOURCED_ERROR_NONE;
908 void finalize_quotas(void)
910 finalize_statement(&insert_stmt);
911 finalize_statement(&select_stmt);
912 finalize_statement(&clear_effective_stmt);
913 g_tree_destroy(quotas);