diff options
author | Sangjung Woo <sangjung.woo@samsung.com> | 2015-06-09 11:11:50 +0900 |
---|---|---|
committer | Sangjung Woo <sangjung.woo@samsung.com> | 2015-06-09 15:10:34 +0900 |
commit | 625b8675d47177c383d19413ba1e12454a847864 (patch) | |
tree | 5d8f050818f580e3edc9c48de11f5bbb6508f30f | |
parent | 315e4e002c6c8cf9398c5d0625a4fac0e40830db (diff) | |
download | resourced-tizen_3.0.2015.q2_common.tar.gz resourced-tizen_3.0.2015.q2_common.tar.bz2 resourced-tizen_3.0.2015.q2_common.zip |
Overall rebase: add up-to-date resourced codes to public reposubmit/tizen_3.0.2015.q2_common/20150615.075539submit/tizen/20150610.013710accepted/tizen/wearable/20150610.095757accepted/tizen/tv/20150610.095819accepted/tizen/mobile/20150610.095811accepted/tizen/common/20150610.092527accepted/tizen/3.0.2015.q2/common/20150615.091834tizen_3.0.2015.q2_commonaccepted/tizen_3.0.2015.q2_common
* Rebase work until 'memory: disabled swap and vmpressure modules for
mobile profile (c4e54093)'
* Support runtime-info API
* Fix the minor bugs & turn up each module
* Support aarch64, arm32 and emulator
Change-Id: I8dfc1d1d3aa9bcdd89f7632752d968fe3a854484
Signed-off-by: Sangjung Woo <sangjung.woo@samsung.com>
118 files changed, 7565 insertions, 5057 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 3496ac83..88f5914c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,14 +39,9 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) #following section is needed for pkg-config *.in file SET(INCLUDEDIR ${PREFIX}/include) SET(PC_NAME lib${RESOURCED}) -SET(PC_NAME_DEPRECATED ${fw_name}) -SET(PC_REQUIRED "glib-2.0 vconf vconf-internal-keys sqlite3 dlog edbus eina") +SET(PC_REQUIRED "glib-2.0 vconf vconf-internal-keys sqlite3 dlog eina edbus") -SET(PC_PROVIDED_LIBS "-l${PROC-STAT}") - -IF("${NETWORK_MODULE}" STREQUAL "ON") - SET(PC_PROVIDED_LIBS "${PC_PROVIDED_LIBS} -l${NETWORK} -l${RESOURCED}") -ENDIF() +SET(PC_PROVIDED_LIBS "-l${PROC-STAT} -l${RESOURCED}") IF("${POWERTOP_MODULE}" STREQUAL "ON") SET(PC_PROVIDED_LIBS "${PC_PROVIDED_LIBS} -l${POWERTOP-WRAPPER}" ) @@ -61,16 +56,11 @@ CONFIGURE_FILE( @ONLY ) -CONFIGURE_FILE( - ${fw_name}.pc.in - ${CMAKE_SOURCE_DIR}/${fw_name}.pc - @ONLY -) - #init variables with sources SET(DATA_DIR ${CMAKE_SOURCE_DIR}/data) SET(CMAKELISTS_DIR ${CMAKE_SOURCE_DIR}/CMakeLists) SET(INCLUDE_COMMON_DIR ${CMAKE_SOURCE_DIR}/src/common) +SET(INCLUDE_MEMORY_DIR ${CMAKE_SOURCE_DIR}/src/memory) SET(INCLUDE_PUBLIC_DIR ${CMAKE_SOURCE_DIR}/include) SET(RESOURCED_INCLUDEDIR ${INCLUDE_COMMON_DIR} ${INCLUDE_PUBLIC_DIR}) @@ -93,8 +83,8 @@ SET(VIP_SOURCE_DIR ${SOURCE_DIR}/vip-agent) SET(TIMER_SOURCE_DIR ${SOURCE_DIR}/timer-slack) INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) -INSTALL(FILES ${CMAKE_SOURCE_DIR}/${fw_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system.d) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.rule DESTINATION /etc/smack/accesses2.d) FILE(MAKE_DIRECTORY ${RESOURCED}-cmake) CONFIGURE_FILE(${CMAKELISTS_DIR}/${RESOURCED}.txt ${RESOURCED}-cmake/CMakeLists.txt COPYONLY) @@ -106,6 +96,4 @@ IF("${POWERTOP_MODULE}" STREQUAL "ON") ADD_SUBDIRECTORY(${POWERTOP-WRAPPER_SOURCE_DIR}) ENDIF() ADD_SUBDIRECTORY(${MEMPS_SOURCE_DIR}) -IF("${NETWORK_MODULE}" STREQUAL "ON") ADD_SUBDIRECTORY(${NETWORK_SOURCE_DIR}) -ENDIF() diff --git a/CMakeLists/resourced.txt b/CMakeLists/resourced.txt index 2d45126a..92a7ecba 100644 --- a/CMakeLists/resourced.txt +++ b/CMakeLists/resourced.txt @@ -27,6 +27,7 @@ SET (HEADERS ${INCLUDE_COMMON_DIR}/module-data.h ${INCLUDE_COMMON_DIR}/module.h ${INCLUDE_COMMON_DIR}/swap-common.h + ${INCLUDE_MEMORY_DIR}/helper.h ) SET (SOURCES @@ -38,20 +39,24 @@ SET (SOURCES ${COMMON_SOURCE_DIR}/file-helper.c ${COMMON_SOURCE_DIR}/module-data.c ${COMMON_SOURCE_DIR}/module.c - ${NETWORK_SOURCE_DIR}/counter.c ${PROC-STAT_SOURCE_DIR}/proc-handler.c ${PROC-STAT_SOURCE_DIR}/proc-main.c ${PROC-STAT_SOURCE_DIR}/proc-noti.c ${PROC-STAT_SOURCE_DIR}/proc-process.c ${PROC-STAT_SOURCE_DIR}/proc-monitor.c + ${PROC-STAT_SOURCE_DIR}/proc-usage-stats.c + ${PROC-STAT_SOURCE_DIR}/proc-usage-stats-helper.c ${RESOURCED_SOURCE_DIR}/init.c ${RESOURCED_SOURCE_DIR}/main.c + ${MEMORY_SOURCE_DIR}/helper.c ) INSTALL(FILES ${DATA_DIR}/${EXCLUDE_LIST_FILE_NAME} DESTINATION /usr/etc) #network module IF("${NETWORK_MODULE}" STREQUAL "ON") - + SET(SOURCES ${SOURCES} + ${NETWORK_SOURCE_DIR}/counter.c + ) IF("${DATAUSAGE_TYPE}" STREQUAL "NFACCT") SET(CONFIG_DATAUSAGE_NFACCT 1) SET(SOURCES ${SOURCES} @@ -62,6 +67,17 @@ IF("${NETWORK_MODULE}" STREQUAL "ON") SET(SOURCES ${SOURCES} ${NETWORK_SOURCE_DIR}/generic-netlink.c ${NETWORK_SOURCE_DIR}/ktgrabber-restriction.c + ${NETWORK_SOURCE_DIR}/ktgrabber-parser.c + ) + ENDIF() + + IF("${WEARABLE_NOTI}" STREQUAL "ON") + SET(SOURCES ${SOURCES} + ${NETWORK_SOURCE_DIR}/notification-wearable.c + ) + ELSE() + SET(SOURCES ${SOURCES} + ${NETWORK_SOURCE_DIR}/notification-mobile.c ) ENDIF() @@ -75,20 +91,25 @@ IF("${NETWORK_MODULE}" STREQUAL "ON") ${NETWORK_SOURCE_DIR}/datausage-common.c ${NETWORK_SOURCE_DIR}/iface-cb.c ${NETWORK_SOURCE_DIR}/nl-helper.c - ${NETWORK_SOURCE_DIR}/notification.c ${NETWORK_SOURCE_DIR}/restriction-handler.c ${NETWORK_SOURCE_DIR}/restriction-helper.c ${NETWORK_SOURCE_DIR}/restriction-local.c ${NETWORK_SOURCE_DIR}/tethering-restriction.c + ${NETWORK_SOURCE_DIR}/db-guard.c ) - IF ("${TETHERING_FEATURE}" STREQUAL "ON") - SET(TETHERING_FEATURE 1) - ENDIF() - INSTALL(FILES ${NETWORK_SOURCE_DIR}/network.conf DESTINATION /etc/resourced) + INSTALL(FILES ${NETWORK_SOURCE_DIR}/500.resourced-datausage.patch.sh + DESTINATION /etc/opt/upgrade) + + SET (REQUIRES_LIST ${REQUIRES_LIST} + openssl + tapi + ) + + ENDIF() IF("${VIP_AGENT}" STREQUAL "ON") @@ -121,15 +142,12 @@ ENDIF() IF("${MEMORY_MODULE}" STREQUAL "ON") SET(SOURCES ${SOURCES} ${MEMORY_SOURCE_DIR}/lowmem-dbus.c + ${MEMORY_SOURCE_DIR}/memcontrol.c ) - IF("${MEMORY_CGROUP}" STREQUAL "ON") - SET(SOURCES ${SOURCES} - ${MEMORY_SOURCE_DIR}/vmpressure-lowmem-handler.c - ) + IF("${MEMORY_VMPRESSURE}" STREQUAL "ON") + SET(SOURCES ${SOURCES} ${MEMORY_SOURCE_DIR}/vmpressure-lowmem-handler.c) ELSE() - SET(SOURCES ${SOURCES} - ${MEMORY_SOURCE_DIR}/lowmem-handler.c - ) + SET(SOURCES ${SOURCES} ${MEMORY_SOURCE_DIR}/lowmem-handler.c) ENDIF() ADD_DEFINITIONS("-DMEMORY_SUPPORT") ENDIF() @@ -162,9 +180,10 @@ SET (REQUIRES_LIST ${REQUIRES_LIST} vconf vconf-internal-keys ecore-file + eina edbus + bundle libsystemd-daemon - eina ) IF("${LOGGING_MODULE}" STREQUAL "ON") @@ -180,7 +199,7 @@ FOREACH(flag ${daemon_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag} -pthread") ENDFOREACH(flag) -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE") CONFIGURE_FILE(${INCLUDE_COMMON_DIR}/config.h.in ${INCLUDE_COMMON_DIR}/config.h) @@ -198,7 +217,7 @@ TARGET_LINK_LIBRARIES(cgroup-test ${daemon_pkgs_LDFLAGS} "-L/lib/ -lrt") ADD_EXECUTABLE (${PROJECT_NAME} ${HEADERS} ${SOURCES}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} - ${daemon_pkgs_LDFLAGS} + ${daemon_pkgs_LDFLAGS} "-pie -lrt" ) IF("${NETWORK_MODULE}" STREQUAL "ON") @@ -208,7 +227,7 @@ IF("${NETWORK_MODULE}" STREQUAL "ON") storage settings net-cls - roaming + telephony net-iface ) @@ -241,6 +260,7 @@ ENDIF() IF("${SWAP_MODULE}" STREQUAL "ON") INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/scripts/resourced-zram.sh DESTINATION bin) + INSTALL(FILES ${SWAP_SOURCE_DIR}/swap.conf DESTINATION /etc/resourced) ENDIF() IF("${CPU_MODULE}" STREQUAL "ON") diff --git a/data/traffic_db.sql b/data/traffic_db.sql index 628e6695..fec882b5 100644 --- a/data/traffic_db.sql +++ b/data/traffic_db.sql @@ -9,10 +9,13 @@ CREATE TABLE IF NOT EXISTS statistics ( is_roaming INT, hw_net_protocol_type INT, ifname TEXT, - PRIMARY KEY (binpath, time_stamp, iftype) + reserved TEXT, + imsi TEXT, + ground INT, + PRIMARY KEY (binpath, time_stamp, iftype, imsi) ); -CREATE INDEX IF NOT EXISTS binpath_st_idx ON statistics(binpath, iftype); +CREATE INDEX IF NOT EXISTS binpath_st_idx ON statistics(binpath, iftype, imsi); CREATE TABLE IF NOT EXISTS quotas ( binpath TEXT, @@ -24,10 +27,13 @@ CREATE TABLE IF NOT EXISTS quotas ( start_time BIGINT, iftype INT, roaming INT, - PRIMARY KEY(binpath, iftype, roaming) + reserved TEXT, + imsi TEXT, + ground INT, + PRIMARY KEY(binpath, iftype, roaming, imsi, ground) ); -CREATE INDEX IF NOT EXISTS binpath_qt_idx ON quotas(binpath, iftype); +CREATE INDEX IF NOT EXISTS binpath_qt_idx ON quotas(binpath, iftype, imsi); CREATE TABLE IF NOT EXISTS effective_quotas ( binpath TEXT, @@ -38,10 +44,12 @@ CREATE TABLE IF NOT EXISTS effective_quotas ( iftype INT, roaming INT, state INT DEFAULT 0, - PRIMARY KEY (binpath, iftype, start_time, finish_time, roaming) + reserved TEXT, + imsi TEXT, + PRIMARY KEY (binpath, iftype, start_time, finish_time, roaming, imsi) ); -CREATE INDEX IF NOT EXISTS binpath_effective_quotas_idx ON effective_quotas(binpath, iftype); +CREATE INDEX IF NOT EXISTS binpath_effective_quotas_idx ON effective_quotas(binpath, iftype, imsi); CREATE TABLE IF NOT EXISTS restrictions ( binpath TEXT, @@ -51,16 +59,20 @@ CREATE TABLE IF NOT EXISTS restrictions ( rst_state INT, quota_id INT, roaming INT, - PRIMARY KEY (binpath, iftype) + reserved TEXT, + ifname TEXT, + PRIMARY KEY (binpath, iftype, ifname, quota_id) ); -CREATE INDEX IF NOT EXISTS binpath_restrictions_idx ON restrictions(binpath, iftype); +CREATE INDEX IF NOT EXISTS binpath_restrictions_idx ON restrictions(binpath, iftype, ifname); CREATE TABLE IF NOT EXISTS iface_status ( update_time BIGINT, iftype INT, ifstatus INT, - PRIMARY KEY (update_time) + reserved TEXT, + ifname TEXT, + PRIMARY KEY (update_time, iftype, ifstatus) ); CREATE INDEX IF NOT EXISTS update_tm_if_idx ON iface_status(update_time, iftype, ifstatus); diff --git a/docs/func.txt b/docs/func.txt index b1ce4046..1c76d93a 100644 --- a/docs/func.txt +++ b/docs/func.txt @@ -272,9 +272,9 @@ int datausage_quota_init(sqlite3 *db) Initializes queries used in data usage quota functions. -------------------------------------------------------------------------------- -u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create) +u_int32_t get_classid_by_app_id(const char *pkg_name, int create) -Converts application package name to network class. +Converts application id to network class. pkg_name - zero-terminated string containing the package name create - if non-zero attempts to create the cgroup for pkg_name before fetching network class ID diff --git a/include/data_usage.h b/include/data_usage.h index e9bc2152..6429f06a 100755 --- a/include/data_usage.h +++ b/include/data_usage.h @@ -121,6 +121,7 @@ typedef struct { int snd_warning_limit; int rcv_warning_limit; resourced_roaming_type roaming; + char *ifname; } resourced_net_restrictions; /** @@ -199,10 +200,11 @@ typedef struct { int64_t rcv_quota; int snd_warning_threshold; int rcv_warning_threshold; - resourced_state_t quota_type; + resourced_state_t quota_type; /* TODO rename to ground */ resourced_iface_type iftype; time_t *start_time; resourced_roaming_type roaming_type; + const char *imsi; } data_usage_quota; /** @@ -218,8 +220,10 @@ typedef struct { */ struct datausage_quota_reset_rule { const char *app_id; + const char *imsi; resourced_iface_type iftype; resourced_roaming_type roaming; + resourced_state_t quota_type; }; /** @@ -247,12 +251,13 @@ typedef struct { typedef struct { const char *app_id; const char *ifname; + const char *imsi; resourced_iface_type iftype; resourced_tm_interval *interval; - resourced_common_info foreground; - resourced_common_info background; + resourced_counters cnt; resourced_roaming_type roaming; resourced_hw_net_protocol_type hw_net_protocol_type; + resourced_state_t ground; } data_usage_info; /** @@ -298,6 +303,7 @@ typedef struct { */ typedef struct { const char *app_id; + const char *ifname; resourced_iface_type iftype; resourced_restriction_state rst_state; int rcv_limit; @@ -328,6 +334,7 @@ typedef resourced_cb_ret(*resourced_restriction_cb)( typedef struct { unsigned char version; char *app_id; + const char *imsi; resourced_iface_type iftype; resourced_tm_interval *interval; resourced_connection_period_type connection_state; diff --git a/include/rd-network.h b/include/rd-network.h deleted file mode 100755 index 4ebb2816..00000000 --- a/include/rd-network.h +++ /dev/null @@ -1,936 +0,0 @@ -/* - * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef __RD_NETWORK_H__ -#define __RD_NETWORK_H__ - -#include <sys/types.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Enumeration for return type - */ -typedef enum { - NETWORK_ERROR_NONMONITOR = -9, /** < Process don't show watchdog popup */ - NETWORK_ERROR_NOTIMPL = -7, /**< Not implemented yet error */ - NETWORK_ERROR_UNINITIALIZED = -6, /**< Cgroup doen't mounted or daemon not started */ - NETWORK_ERROR_NO_DATA = -5, /**< Success, but no data */ - NETWORK_ERROR_INVALID_PARAMETER = -4,/**< Invalid parameter */ - NETWORK_ERROR_OUT_OF_MEMORY = -3, /**< Out of memory */ - NETWORK_ERROR_DB_FAILED = -2, /**< Database error */ - NETWORK_ERROR_FAIL = -1, /**< General error */ - NETWORK_ERROR_NONE = 0 /**< General success */ -} network_error_e; - - -/** - * @brief Enumeration for return type of the callback - */ -typedef enum { - NETWORK_CANCEL = 0, /**< cancel */ - NETWORK_CONTINUE = 1, /**< continue */ -} network_cb_ret_e; - -/** - * @brief Enumeration for the monitored process state - */ -typedef enum { - NETWORK_STATE_UNKNOWN = 0, - NETWORK_STATE_FOREGROUND = 1 << 1, /** < foreground state */ - NETWORK_STATE_BACKGROUND = 1 << 2, /** < background state */ - NETWORK_STATE_LAST_ELEM = 1 << 3 -} network_state_e; - -/** - * @brief Enumeration for network restriction state - */ -typedef enum { - NETWORK_RESTRICTION_UNDEFINDED, - NETWORK_RESTRICTION_ACTIVATED, /** < restriction activated - means it - was sent to kernel */ - NETWORK_RESTRICTION_EXCLUDED, /** < restriction has been excluded - - means it was sent to kernel as - excluded */ - NETWORK_RESTRICTION_REMOVED, /** < restriction has been removed */ - - NETWORK_RESTRICTION_MAX_VALUE -} network_restriction_state; - -/** - * @brief Enumeration for network interface types - */ -typedef enum { - NETWORK_IFACE_UNKNOWN, /**< undefined iface */ - NETWORK_IFACE_DATACALL, /**< mobile data */ - NETWORK_IFACE_WIFI, /**< wifi data */ - NETWORK_IFACE_WIRED, /**< wired interface */ - NETWORK_IFACE_BLUETOOTH, /**< bluetooth interface */ - NETWORK_IFACE_ALL, /**< enumerate all network interface types */ - NETWORK_IFACE_LAST_ELEM -} network_iface_e; - -/** - * @brief Structure for time interval - * @details It's time interval. Zero interval since 0 til 0 means entires interval. - */ -typedef struct { - time_t from; - time_t to; -} network_tm_interval_s; - -/** - * @brief Enumeration for network connection period type - * @details Last received/sent mean counting data from the first connection of each interface - */ -typedef enum { - NETWORK_CON_PERIOD_UNKNOWN, /**< Undefined period */ - NETWORK_CON_PERIOD_LAST_RECEIVED_DATA, /**< Last received data */ - NETWORK_CON_PERIOD_LAST_SENT_DATA, /**< Last sent data */ - NETWORK_CON_PERIOD_TOTAL_RECEIVED_DATA, /**< Total received data */ - NETWORK_CON_PERIOD_TOTAL_SENT_DATA, /**< Total sent data */ - NETWORK_CON_PERIOD_LAST_ELEM -} network_connection_period_e; - -/** - * @brief Enumeration for network roaming type - */ -typedef enum { - NETWORK_ROAMING_UNKNOWN, /**< can't define roaming - roaming unknown */ - NETWORK_ROAMING_ENABLE, /**< in roaming */ - NETWORK_ROAMING_DISABLE, /**< not in roaming */ - NETWORK_ROAMING_LAST_ELEM, -} network_roaming_e; - -/** - * @brief Enumeration for hardware network protocol types - */ -typedef enum { - NETWORK_PROTOCOL_NONE, /**< Network unknown */ - NETWORK_PROTOCOL_DATACALL_NOSVC, /**< Network no service */ - NETWORK_PROTOCOL_DATACALL_EMERGENCY, /**< Network emergency */ - NETWORK_PROTOCOL_DATACALL_SEARCH, /**< Network search 1900 */ - NETWORK_PROTOCOL_DATACALL_2G, /**< Network 2G */ - NETWORK_PROTOCOL_DATACALL_2_5G, /**< Network 2.5G */ - NETWORK_PROTOCOL_DATACALL_2_5G_EDGE, /**< Network EDGE */ - NETWORK_PROTOCOL_DATACALL_3G, /**< Network UMTS */ - NETWORK_PROTOCOL_DATACALL_HSDPA, /**< Network HSDPA */ - NETWORK_PROTOCOL_DATACALL_LTE, /**< Network LTE */ - NETWORK_PROTOCOL_MAX_ELEM -} network_hw_net_protocol_e; - -/** - * @brief Enumeration for the boolean option - * @details Description of the boolean option for enabling/disabling - * network interfaces and enabling/disabling some behaviar - */ -typedef enum { - NETWORK_OPTION_UNDEF, - NETWORK_OPTION_ENABLE, - NETWORK_OPTION_DISABLE -} network_option_e; - -/** - * @brief Structure for network option - * @details Set of the options. - * version - contains structure version - * wifi - enable/disable wifi, NETWORK_OPTION_UNDEF to leave option as is - * datacall - enable/disable datacall, NETWORK_OPTION_UNDEF to leave option as is - * network_timer - set period of the updating data from the kernel, - * 0 to leave option as is - * datacall_logging - enable/disable datacall_logging, - * NETWORK_OPTION_UNDEF to leave option as is - */ -typedef struct { - unsigned char version; - network_option_e wifi; - network_option_e datacall; - time_t network_timer; - network_option_e datacall_logging; -} network_option_s; - -/** - * @brief Set options, daemon will handle option setting. - * @param[in] options The network state option - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_option_s - * @see #network_get_option - */ -network_error_e network_set_option(const network_option_s *options); - -/** - * @brief Get performance control options. - * @param[out] options The network state option - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_option_s - * @see #network_set_option - */ -network_error_e network_get_option(network_option_s *options); - -/** - * @brief Make cgroup and put in it the given pid and generated classid - * @details If cgroup already exists function just put pid in it. - * @param[in] pid Process, that will be added to cgroup pkg name - * @param[in] pkg_name Package name - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_get_classid_by_pkg_name - */ -network_error_e network_make_cgroup_with_pid(const int pid, - const char *pkg_name); - -/** - * @brief Get classid from cgroup with name pkg_name - * @param[in] pkg_name Name of the cgroup - * @param[in] create In case of true - create cgroup if it's not exists - * @return a positive value is classid, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_make_cgroup_with_pid - */ -u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create); - -/** - * @brief Structure for network restriction information - * @details - * rs_type: foreground or background process - * iftype - interface type to apply restriction - * send_limit - amount number of engress bytes allowed for restriction - * rcv_limit - amount number of ingress bytes allowed for restriction - * old behaviour for send_limit & rcv_limit was 0 - * snd_warning_limit - threshold for warning notification on engress bytes - * rcv_warning_limit - threshold for warning notification on ingress bytes - * value - WARNING_THRESHOLD_UNDEF means no threshold - * this limit is different from quota warning threshold, - * threshold means remaining, limit means occupaied - */ -typedef struct { - network_state_e rs_type; - network_iface_e iftype; - int send_limit; - int rcv_limit; - int snd_warning_limit; - int rcv_warning_limit; -} network_restriction_s; - -/** - * @brief Enumeration for restriction counter - */ -typedef struct { - long incoming_bytes; - long outgoing_bytes; -} network_counter_s; - -/** - * @brief Enumeration for holding data usage information - */ -typedef struct { - network_counter_s cnt; - network_restriction_s rst; -} network_common_info; - -/** - * @brief Set restriction information - * @details Set and apply restriction for application. - * It will create new restriction or modify existing. - * @param[in] app_id Application identifier, it's package name now - * @param[in] restriction Restriction to apply for application in foreground mode - * At least one of the restriction should be setted. - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_restriction_s - * @see #network_remove_restriction - * @see #network_remove_restriction_by_iftype - */ -network_error_e network_set_restriction(const char *app_id, - const network_restriction_s *restriction); - -/** - * @brief Structure for information on restrictions. - * @details - * app_id - application identification - copy it if you in - * callback function, don't store raw pointer on it - * iftype - type of network interface - */ -typedef struct { - const char *app_id; - network_iface_e iftype; - network_restriction_state rst_state; - int rcv_limit; - int send_limit; -} network_restriction_info_s; - -/** - * @brief callback for processing information of restrictions - */ -typedef network_cb_ret_e(*network_restriction_cb)( - const network_restriction_info_s *info, void *user_data); - -/** - * @brief Restrictions enumerate function - * @param[in] restriction_db The callback is called for each application that restrcited now - * @param[in] user_data User data will be passed to the callback function - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_restriction_cb - */ -network_error_e network_restriction_foreach(network_restriction_cb restriction_cb, - void *user_data); - -/** - * @brief Remove existing restriction for application - * @details Remove existing restriction for application - * It will delete restriction rule in kernel - * @param[in] app_id Application identifier, it's package name - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_set_restriction - * @see #network_remove_restriction_by_iftype - */ -network_error_e network_remove_restriction(const char *app_id); - -/** - * @brief Remove existing restriction for application from interface type - * @details Remove existing restriction for application - * It will delete restriction rule in kernel - * @param[in] app_id Application identifier, it's package name - * @param[in] iftype Interface type - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_iface_e - * @see #network_set_restriction - * @see #network_remove_restriction_by_iftype - */ -network_error_e network_remove_restriction_by_iftype(const char *app_id, - const network_iface_e iftype); - -/** - * @brief Exclude restriction for application - * @details Exclude restriction for application - * It will exclude restriction rule in kernel - * @param[in] app_id Application identifier, it's package name - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_OK Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_set_restriction - * @see #network_exclude_restriction_by_iftype - */ -network_error_e network_exclude_restriction(const char *app_id); - -/** - * @brief Exclude restriction for application from interface type - * @details Exclude restriction for application - * It will exclude restriction rule in kernel - * @param[in] app_id Application identifier, it's package name - * @param[in] iftype Interface type - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_OK Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_iface_e - * @see #network_set_restriction - * @see #network_exclude_restriction_by_iftype - */ -network_error_e network_exclude_restriction_by_iftype( - const char *app_id, const network_iface_e iftype); - -/** - * @brief Structure for network activity information - */ -typedef struct { - int type; /*<< ingress/egress */ - char *appid; - int iftype; - int bytes; -} network_activity_info_s; - -/** - * @brief callback for network activity information of packet - */ -typedef network_cb_ret_e(*network_activity_cb)(network_activity_info_s *info); - -/** - * @brief Register activity callback - * @details This function registering callback which invokes per every packet. - * Function creates new reading thread and returns. - * @param[in] activity_cb Invoked per every packet with NET_ACTIVITY channel - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_activity_cb - */ -network_error_e network_register_activity_cb(network_activity_cb activity_cb); - -/** - * @brief After invoking this function, application will be in the monitored scope - * @details It creates an appropriate cgroup, - * it generates classid for the network performance control. - * It creates a unit file for the systemd. - * @param[in] app_id Application identifier, it's package name now - * @param[in] pid Pid to put in to cgroup, or self pid of 0 - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - */ -network_error_e network_join_app_performance(const char *app_id, const pid_t pid); - -/** - * @brief Update the resourced counters and stores it in the database. - * @details Updates going asynchronyusly, it mean client can't be sure was - * counters updated or not after this function finished. - * To handle finish of the update process client need to - * regist callback function - * @see network_register_update_cb. - * Next counters updating will procced according to resourced - * update period, unless another network_update_statisitcs is - * not invoked. - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - */ -network_error_e network_update_statistics(void); - -/* - * @brief Counters update information - * @details This structure is needed to prevent client API from modification - * in case of any information about update will be required. - */ -struct network_update_info { - /*dummy content*/ -}; - -/** - * @brief Callback for update counters - */ -typedef network_cb_ret_e(*network_update_cb)( - const struct network_update_info *info, - void *user_data); - -/** - * @brief Register callback for update counters. - * @details Callback function will be called if - * network_update_statistics is requested. - * To stop callbacks invocation return NETWORK_CANCEL from - * callback function or call @see network_unregister_update_cb. - * - * @param[in] user_data pointer to any data given to callback function. - * Memory area should not be released until callback is unregistered. - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @code - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <rd-network.h> - -network_cb_ret_e network_update(const struct network_update_info *info, - void *user_data) -{ - char buf[50]; - char *str_data = user_data; - printf("Callback updated. Stop yes/no?: "); - scanf("%s", buf); - if (strcmp(buf, "yes") == 0) - return NETWORK_CANCEL; - - printf("user data is %s\n", user_data); - - return NETWORK_CONTINUE; -} - -int main(void) -{ - network_error_e ret; - - ecore_init(); - - char *user_data = (char *)malloc(1024); - - strcpy(user_data, "hello"); - - ret = network_register_update_cb(network_update, (void *)user_data); - - network_update_statistics(); - - ecore_main_loop_begin(); - - free(user_data); - ecore_shutdown(); -} - - * @endcode - */ -network_error_e network_register_update_cb(network_update_cb update_cb, - void *user_data); - -/** - * @brief Unregister update callback. - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - */ -void network_unregister_update_cb(void); - -/** - * @brief Structure for selection rule applied - */ -typedef struct { - unsigned char version; - time_t from; - time_t to; - network_iface_e iftype; - int granularity; -} network_selection_rule_s; - -/** - * @brief Bundle structure for bringing all together application identification and properties - * @details - * app_id - application identification - copy it if you in - * callback function, don't store raw pointer on it - * iface - interface name, NULL means all interfaces, - * don't store raw pointer on it in the callback function, copy it by value - * interval - time interval for given result, NULL means entire interval - * foreground - foreground restrictions and counters - * background - background restrictions and counters - */ -typedef struct { - const char *app_id; - network_iface_e iftype; - network_tm_interval_s *interval; - network_common_info foreground; - network_common_info background; - network_roaming_e roaming; - network_hw_net_protocol_e hw_net_protocol_type; -} network_info_s; - -/** - * @brief Callback for enumerate counters and restrictions - */ -typedef network_cb_ret_e(*network_info_cb) (const network_info_s *info, - void *user_data); - -/** - * @brief Data usage enumerate function - * @details The callback is called for each application that used network - * in between timestamps specified. - * If interface name is not specified, each application will only appear - * once with the total traffic used over all interfaces. - * @param[in] rule Selection rule - * @param[in] info_cb The callback is called for each application - * that used network in between timestamps specified - * @param[in] user_data User data will be passed to the callback function - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_selection_rule_s - * @see #network_info_cb - * @see #network_details_foreach - */ -network_error_e network_foreach(const network_selection_rule_s *rule, - network_info_cb info_cb, void *user_data); - -/** - * @brief Data usage details enumerate function - * @detail - * If interface name is specified in rule, the callback will be called - * exactly 1 time with the total traffic counts for that interface - * by specified application in the specified time interval. - * If interface name is not specified, the callback will be called once - * for each interface used by application during the specified interval. - * It could be 0 if the application did not use any network interfaces - * during that period. - * @param[in] app_id Application id - * @param[in] rule Selection rule - * @param[in] info_cb The callback is called for each application - * that used network in between timestamps specified - * @param[in] user_data User data will be passed to the callback function - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_selection_rule_s - * @see #network_info_cb - * @s22 #network_foreach - */ -network_error_e network_details_foreach(const char *app_id, - network_selection_rule_s *rule, - network_info_cb info_cb, - void *user_data); - -/** - * @brief Structure for reset rule - * @details It's statistics erasing description. - * app_id - Erase statistics per appropriate app_id. - * app_id can be NULL in this case erasing all datas - * iftype - Erase statistics per appropriate network interface type - * #network_iface_e, if iftype is NETWORK_IFACE_LAST_ELEM - erase all - * NETWORK_IFACE_UNKNOW - means undetermined interface on moment of storing data. - * interval - It's time interval, @see network_tm_interval_s. - * It should be set. Zero interval since 0 till 0 means entire interval. - * connection_state - It's mask on time interval. - * Possible variation LAST and TOTAL for send and received data. - */ -typedef struct { - unsigned char version; - char *app_id; - network_iface_e iftype; - network_tm_interval_s *interval; - network_connection_period_e connection_state; -} network_reset_rule_s; - -/** - * @brief Reset data usage information - * @param[in] rule Reset rule. It's statistics erasing description - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_reset_rule_s - */ -network_error_e network_reset(const network_reset_rule_s *rule); - -/** - * @brief Reset filter for quota - * @details - * app_id is mandatory field - * iftype interface type, NETWORK_IFACE_UNKNOWN - * interface is not valid parameter, use NETWORK_IFACE_ALL instead - * roaming_type roaming type - * If user will not specify last 2 fields (UNKNOWN by default), - * neither quota with defined interface nor - * quota with defined roaming state will be removed. - */ -typedef struct { - const char *app_id; - network_iface_e iftype; - network_roaming_e roaming; -} network_quota_reset_rule_s; - -/** - * @brief Remove datausage quota by quota rule - * @param[in] rule reset filter for quota - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_quota_reset_rule_s - * @see #network_set_quota - * @see #network_remove_quota_by_iftype - */ -network_error_e network_remove_quota( - const network_quota_reset_rule_s *rule); - -/** - * @brief Remove datausage quota by quota rule - * @param[in] app_id Application id - * @param[in] iftype Interface type - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_quota_reset_rule_s - * @see #network_set_quota - * @see #network_remove_quota - */ -network_error_e network_remove_quota_by_iftype( - const char *app_id, const network_iface_e iftype); - -/** - * @brief Datausage quota - * @details - * time_period - time interval for quota, use predefined quota - * #network_quota_s_period_t - * snd_quota - quota for outcoming data - * rcv_quota - quota for incoming data - * warning_send_threshold - threshold for warning notification on engress bytes - * warning_rcv_threshold - threshold for warning notification on ingress bytes - * value - WARNING_THRESHOLD_UNDEF means no threshold - * - WARNING_THRESHOLD_DEFAULT means resourced will be - * responsible for evaluation threshold value - * The threshold value is amount of bytes remaining till blocking - * quota_type - at present it can be foreground quota or background - * iftype - network interface type - * start_time - quota processing activation time, if NULL current time is used - */ -typedef struct { - int time_period; - int64_t snd_quota; - int64_t rcv_quota; - int snd_warning_threshold; - int rcv_warning_threshold; - network_state_e quota_type; - network_iface_e iftype; - time_t *start_time; - network_roaming_e roaming_type; -} network_quota_s; - -/** - * @brief Set datausage quota - * @details This function will set time interval based quota for data usage - * Restriction will be applied in case of exceeding of the quota - * during time interval - * @param app_id[in] Application identifier, it's package name - * @param quotas[in] Time interval based restriction for data usage - * - * @return 0 on success, otherwise a negative error value - * @retval #NETWORK_ERROR_NONE Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * @retval #NETWORK_ERROR_NO_DATA Success, but no data - * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started - * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error - * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup - * - * @see #network_set_quota - * @see #network_remove_quota - * @see #network_remove_quota_by_iftype - */ -network_error_e network_set_quota(const char *app_id, - const network_quota_s *quota); - - -/** - * - * This function get restriction state. - * State can be following: - * NETWORK_RESTRICTION_UNDEFINDED - means restriction wasn't set - * NETWORK_RESTRICTION_ACTIVATED - means restriction activated - * NETWORK_RESTRICTION_EXCLUDED - restriction has been excluded - * - * @code - * #include <rd-network.h> - * - * int is_whole_network_restricted() - * { - * network_restriction_state state; - * network_error_r ret_code = network_get_restriction_state( - * RESOURCED_ALL_APP, NETWORK_IFACE_ALL, &state); - * if (ret_code != NETWORK_ERROR_NONE && - * state == NETWORK_RESTRICTION_ACTIVATED) - * return 1; - * return 0; - * } - * - * @endcode - * - * @retval #NETWORK_ERROR_OK Successful - * @retval #NETWORK_ERROR_FAIL General error - * @retval #NETWORK_ERROR_DB_FAILED Database error - * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter - * - * @see #network_iface_e - * @see #network_restriction_state - * @see #network_set_restriction - * @see #network_exclude_restriction_by_iftype - * - */ -network_error_e network_get_restriction_state(const char *pkg_id, - network_iface_e iftype, network_restriction_state *state); - - -#ifdef __cplusplus -} -#endif - -#endif // __RD_NETWORK_H__ diff --git a/packaging/resourced-logging.service b/packaging/resourced-logging.service deleted file mode 100644 index fe0b64dd..00000000 --- a/packaging/resourced-logging.service +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=Logging Initialization -After=resourced.service - -[Service] -Type=oneshot -ExecStartPre=/bin/sleep 1 -ExecStart=/usr/bin/dbus-send --type=signal --system /Org/Tizen/ResourceD/Logging org.tizen.resourced.logging.LoggingInit - -[Install] -WantedBy=graphical.target diff --git a/packaging/resourced.service b/packaging/resourced.service index 12475c78..c9454bcb 100644 --- a/packaging/resourced.service +++ b/packaging/resourced.service @@ -5,6 +5,7 @@ Wants=tizen-system.target [Service] Type=notify +EnvironmentFile=/run/tizen-mobile-env Environment="CGROUP_MEMORY_SWAP=/sys/fs/cgroup/memory/swap" Environment="SWAPFILE=/dev/zram0" ExecStartPre=-/bin/mkdir -pm 755 $CGROUP_MEMORY_SWAP diff --git a/packaging/resourced.spec b/packaging/resourced.spec index ca104137..0b2c5332 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -9,35 +9,42 @@ Source1: resourced.service Source2: resourced-zram.service Source5: resourced_swapoff.service Source6: resourced-cpucgroup.service -Source8: resourced-logging.service %define powertop_state OFF %define cpu_module ON -%define vip_agent_module ON %define timer_slack ON -%define logging_module OFF + +%define vip_agent_module ON + +%define logging_module ON %define logging_memory OFF -%define logging_cpu OFF +%define logging_cpu ON + %define memory_module ON -%define memory_cgroup OFF %define swap_module OFF +%define memory_vmpressure ON + %define network_state OFF -%define telephony_feature OFF %define tethering_feature OFF %if "%{?tizen_profile_name}" == "mobile" %define swap_module OFF + %define memory_vmpressure OFF %define network_state OFF - %define telephony_feature OFF %define tethering_feature OFF - %endif %if "%{?tizen_profile_name}" == "wearable" - %define swap_module OFF + %define swap_module ON + %define memory_vmpressure ON %define network_state OFF - %define telephony_feature OFF +%endif +%if "%{?tizen_profile_name}" == "tv" + %define swap_module ON + %define memory_vmpressure ON + %define network_state OFF + %define tethering_feature OFF %endif %if 0%{?tizen_build_binary_release_type_eng} @@ -59,11 +66,13 @@ BuildRequires: pkgconfig(vconf) BuildRequires: pkgconfig(vconf-internal-keys) BuildRequires: pkgconfig(ecore) BuildRequires: pkgconfig(ecore-file) +BuildRequires: pkgconfig(eina) BuildRequires: pkgconfig(edbus) -BuildRequires: pkgconfig(capi-network-connection) BuildRequires: pkgconfig(libsystemd-daemon) -BuildRequires: pkgconfig(eina) - +BuildRequires: pkgconfig(openssl) +BuildRequires: pkgconfig(bundle) +#only for data types +BuildRequires: pkgconfig(tapi) Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig @@ -72,7 +81,7 @@ BuildRequires: pkgconfig(libsystemd-journal) %endif %description -Resourced (Resource management daemon) +Resourced daemon %package resourced Summary: Resource Daemon @@ -87,15 +96,15 @@ Group: System/Libraries Requires: %{name} = %{version}-%{release} %description -n libresourced -Library of resourced (Resource management daemon) +Library for resourced (Resource Management Daemon) %package -n libresourced-devel Summary: Resource Daemon Library (Development) Group: System/Libraries -Requires: libresourced = %{version}-%{release} +Requires: libresourced = %{version}-%{release} %description -n libresourced-devel -Library (Development) of resourced (Resource management daemon) +Library (development) for resourced (Resource Management Daemon) %prep %setup -q @@ -118,15 +127,29 @@ export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" -%cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DCMAKE_BUILD_TYPE=Release \ +%cmake . \ + -DFULLVER=%{version} \ + -DMAJORVER=${MAJORVER} \ + -DCMAKE_BUILD_TYPE=Release \ -DEXCLUDE_LIST_FILE_NAME=%{exclude_list_file_name} \ - -DEXCLUDE_LIST_FULL_PATH=%{exclude_list_full_path} -DDATABASE_FULL_PATH=%{database_full_path} \ - -DEXCLUDE_LIST_OPT_FULL_PATH=%{exclude_list_opt_full_path} -DNETWORK_MODULE=%{network_state} -DSWAP_MODULE=%{swap_module} \ - -DPOWERTOP_MODULE=%{powertop_state} -DCPU_MODULE=%{cpu_module}\ - -DMEMORY_ENG=%{memory_eng} -DVIP_AGENT=%{vip_agent_module} -DTELEPHONY_FEATURE=%{telephony_feature} \ - -DTIMER_SLACK=%{timer_slack} -DLOGGING_MODULE=%{logging_module} -DLOGGING_MEMORY=%{logging_memory} \ - -DLOGGING_CPU=%{logging_cpu} -DDATAUSAGE_TYPE=NFACCT -DMEMORY_MODULE=%{memory_module} -DMEMORY_CGROUP=%{memory_cgroup} \ - -DTETHERING_FEATURE=%{tethering_feature} + -DEXCLUDE_LIST_FULL_PATH=%{exclude_list_full_path} \ + -DEXCLUDE_LIST_OPT_FULL_PATH=%{exclude_list_opt_full_path} \ + -DVIP_AGENT=%{vip_agent_module} \ + -DSWAP_MODULE=%{swap_module} \ + -DMEMORY_ENG=%{memory_eng} \ + -DMEMORY_VMPRESSURE=%{memory_vmpressure} \ + -DMEMORY_MODULE=%{memory_module} \ + -DMEMORY_VMPRESSURE=%{memory_vmpressure} \ + -DDATABASE_FULL_PATH=%{database_full_path} \ + -DNETWORK_MODULE=%{network_state} \ + -DTELEPHONY_FEATURE=%{telephony_feature} \ + -DDATAUSAGE_TYPE=NFACCT \ + -DLOGGING_MODULE=%{logging_module} \ + -DLOGGING_MEMORY=%{logging_memory} \ + -DLOGGING_CPU=%{logging_cpu} \ + -DPOWERTOP_MODULE=%{powertop_state} \ + -DCPU_MODULE=%{cpu_module} \ + -DTIMER_SLACK=%{timer_slack} make %{?jobs:-j%jobs} @@ -169,13 +192,6 @@ mkdir -p %{buildroot}/usr/share/powertop-wrapper/ cp -p %_builddir/%name-%version/src/powertop-wrapper/header.html %{buildroot}/usr/share/powertop-wrapper %endif -%if %{?logging_module} == ON -#logging service file -mkdir -p %{buildroot}%{_libdir}/systemd/system/graphical.target.wants -install -m 0644 %SOURCE8 %{buildroot}%{_libdir}/systemd/system/resourced-logging.service -ln -s ../resourced-logging.service %{buildroot}%{_libdir}/systemd/system/graphical.target.wants/resourced-logging.service -%endif - %pre resourced if [ "$1" = "2" ]; then # upgrade begins systemctl stop resourced.service @@ -187,12 +203,13 @@ fi init_vconf() { - vconftool set -t bool db/private/resourced/wifi_statistics 1 -i -f -s resourced - vconftool set -t bool db/private/resourced/datacall 1 -i -f -s resourced - vconftool set -t bool db/private/resourced/datacall_logging 1 -i -f -s resourced - vconftool set -t int db/private/resourced/datausage_timer 60 -i -f -s resourced - vconftool set -t string db/private/resourced/new_limit "" -u 5000 -f -s resourced - vconftool set -t string db/private/resourced/delete_limit "" -u 5000 -f -s resourced + vconftool set -t bool db/private/resourced/wifi_statistics 1 -i -f -s tizen::vconf::platform::rw + vconftool set -t bool db/private/resourced/datacall 1 -i -f -s tizen::vconf::platform::rw + vconftool set -t bool db/private/resourced/datacall_logging 1 -i -f -s tizen::vconf::platform::rw + vconftool set -t int db/private/resourced/datausage_timer 60 -i -f -s tizen::vconf::platform::rw + vconftool set -t string db/private/resourced/new_limit "" -u 5000 -f -s tizen::vconf::platform::rw + vconftool set -t string db/private/resourced/delete_limit "" -u 5000 -f -s tizen::vconf::platform::rw + vconftool set -t int db/private/resourced/network_db_entries 0 -i -f -s tizen::vconf::platform::rw } %if %{?network_state} == ON @@ -211,11 +228,15 @@ fi %files -n resourced /usr/share/license/%{name} +/etc/smack/accesses2.d/resourced.rule %attr(-,root, root) %{_bindir}/resourced %if %{?network_state} == ON %config(noreplace) %attr(660,root,app) %{database_full_path} %config(noreplace) %attr(660,root,app) %{database_full_path}-journal /usr/bin/datausagetool + %config /etc/resourced/network.conf + /etc/opt/upgrade/500.resourced-datausage.patch.sh + %attr(700,root,root) /etc/opt/upgrade/500.resourced-datausage.patch.sh %manifest resourced.manifest %else %manifest resourced_nodb.manifest @@ -232,14 +253,11 @@ fi %{_libdir}/systemd/system/graphical.target.wants/resourced-cpucgroup.service %endif %if %{?swap_module} == ON +%config /etc/resourced/swap.conf %{_libdir}/systemd/system/resourced-zram.service %{_libdir}/systemd/system/graphical.target.wants/resourced-zram.service %{_bindir}/resourced-zram.sh %endif -%if %{?logging_module} == ON -%{_libdir}/systemd/system/resourced-logging.service -%{_libdir}/systemd/system/graphical.target.wants/resourced-logging.service -%endif %if %{?vip_agent_module} == ON %config /etc/resourced/vip-process.conf %attr(-,root, root) %{_bindir}/vip-release-agent @@ -262,10 +280,7 @@ fi #proc-stat part %{_libdir}/libproc-stat.so.* #network part -%if %{?network_state} == ON %{_libdir}/libresourced.so.* -%{_libdir}/librd-network.so.* -%endif #powertop-wrapper part %if %{?powertop_state} == ON %{_libdir}/libpowertop-wrapper.so.* @@ -279,13 +294,10 @@ fi %{_includedir}/system/proc_stat.h %{_libdir}/libproc-stat.so #network part -%if %{?network_state} != OFF +%if %{?network_state} == ON %{_includedir}/system/data_usage.h -%{_includedir}/system/rd-network.h -%{_libdir}/libresourced.so -%{_libdir}/librd-network.so -%config /etc/resourced/network.conf %endif +%{_libdir}/libresourced.so #powertop-wrapper part %if %{?powertop_state} == ON %{_includedir}/system/powertop-dapi.h diff --git a/packaging/resourced_swapoff.service b/packaging/resourced_swapoff.service index ff164c89..1b95268b 100644 --- a/packaging/resourced_swapoff.service +++ b/packaging/resourced_swapoff.service @@ -4,6 +4,8 @@ After=tizen-system.target Wants=tizen-system.target [Service] +Type=simple +EnvironmentFile=/run/tizen-system-env ExecStart=/usr/bin/resourced -s 1 -u 60 Restart=always RestartSec=0 diff --git a/resourced.manifest b/resourced.manifest index 56400ee2..f4624b6b 100644 --- a/resourced.manifest +++ b/resourced.manifest @@ -3,10 +3,26 @@ <domain name="resourced"/> <provide> <label name="resourced::db"/> + <label name="resourced::systeminfo"/> </provide> + <request> + <smack request="resourced::systeminfo" type="rw"/> + </request> </define> <assign> <filesystem path="/opt/usr/dbspace/.resourced-datausage.db*" label="resourced::db"/> + <dbus name="org.tizen.resourced" own="resourced" bus="system"> + <node name="/Org/Tizen/ResourceD/Process"> + <interface name="org.tizen.resourced.process"> + <method name="ProcMemoryUsage"> + <annotation name="com.tizen.smack" value="resourced::systeminfo" /> + </method> + <method name="ProcCpuUsage"> + <annotation name="com.tizen.smack" value="resourced::systeminfo" /> + </method> + </interface> + </node> + </dbus> </assign> <request> <domain name="resourced"/> diff --git a/resourced.pc.in b/resourced.pc.in deleted file mode 100644 index 11dfe0e4..00000000 --- a/resourced.pc.in +++ /dev/null @@ -1,16 +0,0 @@ -# Package Information for pkg-config -# -# Copyright (c) 2012 Samsung Electronics Co., Ltd. -# All rights reserved. -# - -libdir=@LIB_INSTALL_DIR@ -includedir=@INCLUDEDIR@ - -Name: @PC_NAME_DEPRECATED@ -Description: Tizen Resource management Daemon -Version: @VERSION@ -Requires: @PC_REQUIRED@ -Libs: -L${libdir} @PC_PROVIDED_LIBS@ -Cflags: @PC_CFLAGS@ - diff --git a/resourced.rule b/resourced.rule index d6cbe41e..f326c031 100644 --- a/resourced.rule +++ b/resourced.rule @@ -1,3 +1,9 @@ -resourced com.samsung.setting::private r -resourced system::vconf_inhouse rw -resourced system-apps r
\ No newline at end of file +resourced tizen::vconf::public::r::platform::rw rw +resourced system-apps r +resourced sys-assert::core rwxat +resourced system::media rwxat +resourced system::media::root rwxat +resourced telephony_framework::api_network r +resourced telephony_framework::api_manager r +resourced telephony_framework::api_modem r +resourced telephony_framework::api_sim r diff --git a/resourced_nodb.manifest b/resourced_nodb.manifest index 3256181b..94675d92 100644 --- a/resourced_nodb.manifest +++ b/resourced_nodb.manifest @@ -1,5 +1,28 @@ <manifest> -<request> - <domain name="_"/> -</request> + <define> + <domain name="resourced"/> + <provide> + <label name="resourced::systeminfo"/> + </provide> + <request> + <smack request="resourced::systeminfo" type="rw"/> + </request> + </define> + <assign> + <dbus name="org.tizen.resourced" own="resourced" bus="system"> + <node name="/Org/Tizen/ResourceD/Process"> + <interface name="org.tizen.resourced.process"> + <method name="ProcMemoryUsage"> + <annotation name="com.tizen.smack" value="resourced::systeminfo" /> + </method> + <method name="ProcCpuUsage"> + <annotation name="com.tizen.smack" value="resourced::systeminfo" /> + </method> + </interface> + </node> + </dbus> + </assign> + <request> + <domain name="resourced"/> + </request> </manifest> diff --git a/src/common/appid-helper.c b/src/common/appid-helper.c index 091a1041..915bd2ec 100644 --- a/src/common/appid-helper.c +++ b/src/common/appid-helper.c @@ -17,15 +17,27 @@ * */ +#include <stdbool.h> #include <stdlib.h> #include <string.h> -#define BASE_NAME_PREFIX "com.samsung." #define DOT_DELIMETER '.' -static int is_base_name(const char *appid) +/* + * no need to extract in case of com.facebook, com.opera. + * org.tizen + * but in case of samsung.Engk10bghd we will do it, + * It's better here to pass appid as is to setting, + * but in this case setting should group it, because + * several appid is possible in one package. + * */ +static bool is_base_name(const char *appid) { - return strstr(appid, BASE_NAME_PREFIX) != NULL; + char *dot = index(appid, DOT_DELIMETER); + if (!dot) + return false; + + return index(dot, DOT_DELIMETER) != NULL; } void extract_pkgname(const char *appid, char *pkgname, diff --git a/src/common/cgroup.c b/src/common/cgroup.c index 9958f22e..3fb06458 100644 --- a/src/common/cgroup.c +++ b/src/common/cgroup.c @@ -81,6 +81,40 @@ resourced_ret_c place_pid_to_cgroup(const char *cgroup_subsystem, return place_pid_to_cgroup_by_fullpath(buf, pid); } +resourced_ret_c place_pidtree_to_cgroup(const char *cgroup_subsystem, + const char *cgroup_name, const int pid) +{ + char buf[MAX_PATH_LENGTH]; + + /*/proc/%d/task/%d/children */ + char child_buf[21 + MAX_DEC_SIZE(int) + MAX_DEC_SIZE(int)]; + char pidbuf[MAX_DEC_SIZE(int)]; + resourced_ret_c ret; + + FILE *f; + + snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name); + /* place parent */ + ret = place_pid_to_cgroup_by_fullpath(buf, pid); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "Failed to put parent process %d into %s cgroup", pid, cgroup_name); + + snprintf(child_buf, sizeof(child_buf), PROC_TASK_CHILDREN, + pid, pid); + f = fopen(child_buf, "r"); + ret_value_msg_if(!f, RESOURCED_ERROR_FAIL, "Failed to get child pids!"); + while (fgets(pidbuf, sizeof(pidbuf), f) != NULL) { + int child_pid = atoi(pidbuf); + ret_value_msg_if(child_pid < 0, RESOURCED_ERROR_FAIL, + "Invalid child pid!"); + resourced_ret_c ret = place_pid_to_cgroup_by_fullpath(buf, child_pid); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "Failed to put parent process %d into %s cgroup", pid, cgroup_name); + + } + return RESOURCED_ERROR_NONE; +} + int cgroup_write_node(const char *cgroup_name, const char *file_name, unsigned int value) { @@ -113,7 +147,11 @@ int make_cgroup_subdir(char* parentdir, char* cgroup_name, int *exists) int cgroup_exists = 0, ret = 0; char buf[MAX_PATH_LENGTH]; - ret = snprintf(buf, sizeof(buf), "%s/%s", parentdir, cgroup_name); + if (parentdir) + ret = snprintf(buf, sizeof(buf), "%s/%s", parentdir, cgroup_name); + else + ret = snprintf(buf, sizeof(buf), "%s", cgroup_name); + ret_value_msg_if(ret > sizeof(buf), RESOURCED_ERROR_FAIL, "Not enought buffer size for %s%s", parentdir, cgroup_name); diff --git a/src/common/cgroup.h b/src/common/cgroup.h index c57bf67d..ea6e5a7a 100644 --- a/src/common/cgroup.h +++ b/src/common/cgroup.h @@ -27,7 +27,9 @@ #ifndef _CGROUP_LIBRARY_CGROUP_H_ #define _CGROUP_LIBRARY_CGROUP_H_ -#define DEFAULT_CGROUP "/sys/fs/cgroup" + +#define DEFAULT_CGROUP "/sys/fs/cgroup" +#define PROC_TASK_CHILDREN "/proc/%d/task/%d/children" /** * @desc Get one unsigned int value from cgroup @@ -78,10 +80,9 @@ int make_cgroup_subdir(char* parentdir, char* cgroup_name, int *cgroup_exists); int mount_cgroup_subsystem(char* source, char* mount_point, char* opts); /** - * @desc mount cgroup, - * @param source -cgroup name - * @param mount_point - cgroup path - * @param opts - mount options + * @desc write pid into cgroup_subsystem/cgroup_name file, + * @param cgroup_subsystem path to /sys/fs/cgroup/subsystem + * @param cgroup_name - name in /sys/fs/cgroup/subsystem/ * @return negative value if error */ resourced_ret_c place_pid_to_cgroup(const char *cgroup_subsystem, @@ -90,4 +91,12 @@ resourced_ret_c place_pid_to_cgroup(const char *cgroup_subsystem, resourced_ret_c place_pid_to_cgroup_by_fullpath(const char *cgroup_full_path, const int pid); +/** + * @desc doing the same as @see place_pid_to_cgroup, + * but also put into cgroup first level child processes + */ +resourced_ret_c place_pidtree_to_cgroup(const char *cgroup_subsystem, + const char *cgroup_name, const int pid); + + #endif /*_CGROUP_LIBRARY_CGROUP_H_*/ diff --git a/src/common/const.h b/src/common/const.h index 4c943964..8e743dfd 100644 --- a/src/common/const.h +++ b/src/common/const.h @@ -20,9 +20,6 @@ /* * @file: const.h * - * @desc Application stat entity - * @version 1.0 - * */ #ifndef _RESOURCED_CONST_H @@ -34,12 +31,12 @@ #define MAX_PATH_LENGTH 512 #define MAX_NAME_LENGTH 256 +#define MAX_IFACE_LENGTH 32 #define COMMA_DELIMETER "," #define COUNTER_UPDATE_PERIOD 60 #define FLUSH_PERIOD 60 -#define STORE_DELAY_INTERVAL 1 #define NONE_QUOTA_ID 0 @@ -56,6 +53,9 @@ enum resourced_reserved_classid { applications */ RESOURCED_TETHERING_APP_CLASSID, /**< it uses in user space logic for counting tethering traffic */ + RESOURCED_FOREGROUND_APP_CLASSID, /* it will used for special cgroup, + blocked cgroup */ + RESOURCED_BACKGROUND_APP_CLASSID, RESOURCED_RESERVED_CLASSID_MAX, }; @@ -64,6 +64,8 @@ enum resourced_counter_state { RESOURCED_FORCIBLY_FLUSH_STATE = 1 << 1, RESOURCED_FORCIBLY_QUIT_STATE = 1 << 2, RESOURCED_NET_BLOCKED_STATE = 1 << 3, + RESOURCED_CHECK_QUOTA = 1 << 4, + RESOURCED_UPDATE_REQUESTED = 1 << 5, }; #endif /* _RESOURCED_CONST_H */ diff --git a/src/common/daemon-options.h b/src/common/daemon-options.h index a046dd6f..3bc60ba2 100644 --- a/src/common/daemon-options.h +++ b/src/common/daemon-options.h @@ -31,7 +31,6 @@ #include <signal.h> struct daemon_opts { - sig_atomic_t is_update_quota; sig_atomic_t datacall_logging; /**< type of rsml_datacall_logging_option */ sig_atomic_t start_daemon; sig_atomic_t update_period; diff --git a/src/common/edbus-handler.c b/src/common/edbus-handler.c index 85b6f275..6bf35730 100644 --- a/src/common/edbus-handler.c +++ b/src/common/edbus-handler.c @@ -35,6 +35,8 @@ struct edbus_list{ char *signal_name; + char *signal_path; + E_DBus_Signal_Handler *handler; }; @@ -66,6 +68,11 @@ static int append_variant(DBusMessageIter *iter, for (ch = (char*)sig, i = 0; *ch != '\0'; ++i, ++ch) { switch (*ch) { + case 'b': + int_type = atoi(param[i]); + dbus_message_iter_append_basic(iter, + DBUS_TYPE_BOOLEAN, &int_type); + break; case 'i': int_type = atoi(param[i]); dbus_message_iter_append_basic(iter, @@ -329,19 +336,20 @@ static void unregister_edbus_signal_handle(void) } int register_edbus_signal_handler(const char *path, const char *interface, - const char *name, E_DBus_Signal_Cb cb) + const char *name, E_DBus_Signal_Cb cb, void *user_data) { Eina_List *search; struct edbus_list *entry; E_DBus_Signal_Handler *handler; EINA_LIST_FOREACH(edbus_handler_list, search, entry) { - if (entry != NULL && strncmp(entry->signal_name, name, strlen(name)) == 0) + if (entry != NULL && strncmp(entry->signal_name, name, strlen(name)) == 0 && + strncmp(entry->signal_path, path, strlen(path)) == 0) return RESOURCED_ERROR_FAIL; } handler = e_dbus_signal_handler_add(edbus_conn, NULL, path, - interface, name, cb, NULL); + interface, name, cb, user_data); if (!handler) { _E("fail to add edbus handler"); @@ -359,19 +367,34 @@ int register_edbus_signal_handler(const char *path, const char *interface, if (!entry->signal_name) { _E("Malloc failed"); - free(entry); - return -1; + goto release_entry; + } + + entry->signal_path = strndup(path, strlen(path)); + if (!entry->signal_path) { + _E("Malloc failed"); + goto release_name; } entry->handler = handler; edbus_handler_list = eina_list_prepend(edbus_handler_list, entry); if (!edbus_handler_list) { _E("eina_list_prepend failed"); - free(entry->signal_name); - free(entry); - return RESOURCED_ERROR_FAIL; + goto release_path; } + return RESOURCED_ERROR_NONE; + +release_path: + free(entry->signal_path); + +release_name: + free(entry->signal_name); + +release_entry: + + free(entry); + return RESOURCED_ERROR_FAIL; } int broadcast_edbus_signal_str(const char *path, const char *interface, @@ -476,6 +499,19 @@ resourced_ret_c edbus_add_methods(const char *path, return RESOURCED_ERROR_NONE; } +resourced_ret_c edbus_message_send(DBusMessage *msg) +{ + DBusPendingCall *pending; + + pending = e_dbus_message_send(edbus_conn, msg, NULL, -1, NULL); + if (!pending) { + _E("sending message over dbus failed, connection disconnected!"); + return RESOURCED_ERROR_FAIL; + } + + return RESOURCED_ERROR_NONE; +} + static void request_name_cb(void *data, DBusMessage *msg, DBusError *error) { DBusError err; diff --git a/src/common/edbus-handler.h b/src/common/edbus-handler.h index e6205212..a789563f 100644 --- a/src/common/edbus-handler.h +++ b/src/common/edbus-handler.h @@ -94,6 +94,9 @@ struct edbus_object { #define SYSTEM_POPUP_PATH_WATCHDOG SYSTEM_POPUP_PATH_NAME"/System" #define SYSTEM_POPUP_IFACE_WATCHDOG SYSTEM_POPUP_BUS_NAME".System" +#define SYSTEM_POPUP_PATH_DATAUSAGE SYSTEM_POPUP_PATH_NAME"/DataUsage" +#define SYSTEM_POPUP_IFACE_DATAUSAGE SYSTEM_POPUP_BUS_NAME".DataUsage" + /* * Deviced */ @@ -104,6 +107,14 @@ struct edbus_object { #define DEVICED_PATH_DISPLAY "/Org/Tizen/System/DeviceD/Display" #define DEVICED_INTERFACE_DISPLAY DEVICED_BUS_NAME".display" +/* + * dump service + */ +#define DUMP_SERVICE_BUS_NAME "org.tizen.system.dumpservice" +#define DUMP_SERVICE_OBJECT_PATH "/Org/Tizen/System/DumpService" +#define DUMP_SERVICE_INTERFACE_NAME DUMP_SERVICE_BUS_NAME + + #define SIGNAL_LCD_ON "LCDOn" #define SIGNAL_LCD_OFF "LCDOff" @@ -130,7 +141,7 @@ int dbus_method_async(const char *dest, const char *path, const char *sig, char *param[]); int register_edbus_signal_handler(const char *path, const char *interface, - const char *name, E_DBus_Signal_Cb cb); + const char *name, E_DBus_Signal_Cb cb, void *user_data); E_DBus_Interface *get_edbus_interface(const char *path); pid_t get_edbus_sender_pid(DBusMessage *msg); int broadcast_edbus_signal_str(const char *path, const char *interface, @@ -140,6 +151,7 @@ int broadcast_edbus_signal(const char *path, const char *interface, resourced_ret_c edbus_add_methods(const char *path, const struct edbus_method *const edbus_methods, const size_t size); +resourced_ret_c edbus_message_send(DBusMessage *msg); int register_edbus_interface(struct edbus_object *object); void edbus_init(void); diff --git a/src/common/macro.h b/src/common/macro.h index f47111c0..73740808 100644 --- a/src/common/macro.h +++ b/src/common/macro.h @@ -37,7 +37,9 @@ #include <stdio.h> #include <config.h> +#ifndef API #define API __attribute__((visibility("default"))) +#endif #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) @@ -68,6 +70,15 @@ static void fn_name(void *data, void __attribute__((__unused__)) *not_used) \ sizeof(type) <= 8 ? 20 : \ sizeof(int[-2*(sizeof(type) > 8)]))) +#define SET_BIT(a, bit) \ + ((a) |= (bit)) + +#define UNSET_BIT(a, bit) \ + ((a) &= ~(bit)) + +#define CHECK_BIT(a, bit) \ + ((a) & (bit)) + #define ret_msg_if(expr, fmt, arg...) do { \ if (expr) { \ _E(fmt, ##arg); \ @@ -108,14 +119,15 @@ static void fn_name(void *data, void __attribute__((__unused__)) *not_used) \ * destination should not be on heap. * Destination will be null terminated */ -#define STRING_SAVE_COPY(destination, source) \ - do { \ +#define STRING_SAVE_COPY(destination, source) do { \ + if (destination && source) { \ size_t null_pos = strlen(source); \ strncpy(destination, source, sizeof(destination)); \ null_pos = sizeof(destination) - 1 < null_pos ? \ sizeof(destination) - 1 : null_pos; \ destination[null_pos] = '\0'; \ - } while(0) + } \ +} while (0) /* FIXME: Do we really need pointers? */ #define array_foreach(key, type, array) \ @@ -141,9 +153,9 @@ static void fn_name(void *data, void __attribute__((__unused__)) *not_used) \ for (elem = head, node = NULL; elem && ((node = elem->data) != NULL); elem = elem->next, node = NULL) #define gslist_for_each_safe(head, elem, elem_next, node) \ - for (elem = head, elem_next = g_list_next(elem), node = NULL; \ + for (elem = head, elem_next = g_slist_next(elem), node = NULL; \ elem && ((node = elem->data) != NULL); \ - elem = elem_next, elem_next = g_list_next(elem), node = NULL) + elem = elem_next, elem_next = g_slist_next(elem), node = NULL) #define DB_ACTION(command) do { \ if ((command) != SQLITE_OK) { \ diff --git a/src/common/module-data.c b/src/common/module-data.c index ce81e5f2..da052ed3 100644 --- a/src/common/module-data.c +++ b/src/common/module-data.c @@ -38,4 +38,5 @@ void init_modules_arg(struct modules_arg *marg, struct daemon_arg *darg) ret_msg_if(marg == NULL || darg == NULL, "Init modules argument failed\n"); marg->opts = darg->opts; + modules_data.darg = darg; } diff --git a/src/common/module-data.h b/src/common/module-data.h index 37f8e9b8..8240f2cb 100644 --- a/src/common/module-data.h +++ b/src/common/module-data.h @@ -40,6 +40,7 @@ struct swap_module_data { struct shared_modules_data { struct counter_arg *carg; + struct daemon_arg *darg; struct swap_module_data swap_data; }; diff --git a/src/common/module.c b/src/common/module.c index 33734bb6..5e8dfafc 100644 --- a/src/common/module.c +++ b/src/common/module.c @@ -113,3 +113,16 @@ void modules_exit(void *data) _E("Fail to deinitialize [%s] module\n", module->name); } } + +void modules_dump(FILE *fp, int mode) +{ + GSList *iter; + const struct module_ops *module; + + gslist_for_each_item(iter, modules_list) { + module = (struct module_ops *)iter->data; + _D("dump [%s] module\n", module->name); + if (module->dump) + module->dump(fp, mode, module->dump_data); + } +} diff --git a/src/common/module.h b/src/common/module.h index 7387cd5d..8ff6c249 100644 --- a/src/common/module.h +++ b/src/common/module.h @@ -38,6 +38,8 @@ struct module_ops { int (*check_runtime_support) (void *data); int (*control) (void *data); int (*status) (void *data); + int (*dump) (FILE *fp, int mode, void *dump_data); + void *dump_data; }; void add_module(const struct module_ops *module); @@ -46,6 +48,7 @@ void remove_module(const struct module_ops *module); void modules_check_runtime_support(void *data); void modules_init(void *data); void modules_exit(void *data); +void modules_dump(FILE *fp, int mode); const struct module_ops *find_module(const char *name); diff --git a/src/common/notifier.h b/src/common/notifier.h index 46f112a9..2d9798f2 100644 --- a/src/common/notifier.h +++ b/src/common/notifier.h @@ -26,16 +26,15 @@ enum notifier_type { RESOURCED_NOTIFIER_APP_RESUME, RESOURCED_NOTIFIER_APP_FOREGRD, RESOURCED_NOTIFIER_APP_BACKGRD, + RESOURCED_NOTIFIER_APP_BACKGRD_ACTIVE, RESOURCED_NOTIFIER_SERVICE_LAUNCH, RESOURCED_NOTIFIER_APP_ACTIVE, RESOURCED_NOTIFIER_APP_INACTIVE, RESOURCED_NOTIFIER_APP_TERMINATE, - RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, RESOURCED_NOTIFIER_SWAP_START, - RESOURCED_NOTIFIER_SWAP_RESTART, - RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, RESOURCED_NOTIFIER_LCD_ON, RESOURCED_NOTIFIER_LCD_OFF, + RESOURCED_NOTIFIER_APP_PRELAUNCH, RESOURCED_NOTIFIER_MAX, }; diff --git a/src/common/swap-common.h b/src/common/swap-common.h index 7760e5c3..e4516fd7 100644 --- a/src/common/swap-common.h +++ b/src/common/swap-common.h @@ -25,32 +25,20 @@ #ifndef __SWAP_COMMON_H__ #define __SWAP_COMMON_H__ -enum swap_status_type { - SWAP_GET_TYPE, - SWAP_GET_CANDIDATE_PID, - SWAP_GET_STATUS, - SWAP_CHECK_PID, - SWAP_CHECK_CGROUP, -}; - enum { SWAP_OFF, SWAP_ON, SWAP_ARG_END, }; -enum { - SWAP_FALSE, - SWAP_TRUE, -}; - #ifdef SWAP_SUPPORT -extern int swap_status(enum swap_status_type type, unsigned long *args); +int swap_check_swap_pid(int pid); #else -static inline int swap_status(enum swap_status_type type, unsigned long *args) +static inline int swap_check_swap_pid(int pid) { - return RESOURCED_ERROR_NONE; + return RESOURCED_ERROR_FAIL; } #endif /* SWAP_SUPPORT */ + #endif /* __SWAP_COMMON_H__ */ diff --git a/src/common/trace.h b/src/common/trace.h index bd56dff5..01cf347b 100644 --- a/src/common/trace.h +++ b/src/common/trace.h @@ -79,4 +79,8 @@ #define ETRACE_ERRNO_MSG(fmt, arg...) \ TRACE_RET_ERRCODE_MSG(E, -errno, fmt, ##arg) +#define LOG_DUMP(fp, fmt, arg...) \ + if (fp) fprintf(fp, fmt, ##arg); \ + else _E(fmt, ##arg); + #endif /* _SYSTEM_RESOURCE_TRACE_H_ */ diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index db677eac..47f413f2 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -28,6 +28,9 @@ #include <errno.h> #include <ctype.h> #include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/syscall.h> #include "notifier.h" #include "proc-main.h" #include "proc-process.h" @@ -47,7 +50,71 @@ #define CPU_CONF_FILE "/etc/resourced/cpu.conf" #define CPU_CONF_SECTION "CONTROL" #define CPU_CONF_PREDEFINE "PREDEFINE" +#define CPU_CONF_BOOTING "BOOTING_PREDEFINE" +#define CPU_CONF_WRT "WRT_PREDEFINE" +#define CPU_CONF_LAZY "LAZY_PREDEFINE" #define CPU_SHARE "/cpu.shares" +#define MAX_PREDEFINED_TASKS 10 +#define CPU_TIMER_INTERVAL 30 +#define CPU_DEFAULT_PRI 0 +#define CPU_BACKGROUND_PRI 1 +#define CPU_CONTROL_PRI 10 +#define CPU_HIGHAPP_PRI -10 + +static Ecore_Timer *cpu_predefined_timer = NULL; + +static inline int ioprio_set(int which, int who, int ioprio) +{ + return syscall(__NR_ioprio_set, which, who, ioprio); +} + +enum +{ + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE, +}; + +enum +{ + IOPRIO_WHO_PROCESS = 1, + IOPRIO_WHO_PGRP, + IOPRIO_WHO_USER, +}; + +#define IOPRIO_CLASS_SHIFT 13 + +enum cpu_control_type { + SET_NONE, + SET_DEFAUT, + SET_BOOTING, + SET_WRT, + SET_LAZY, +}; + +struct controltype { + int type; + pid_t pid; +}; + +struct predefined { + int num; + struct controltype control[MAX_PREDEFINED_TASKS]; +}; + +struct predefined def_list = {0}; + +static int check_predefined(const pid_t pid) +{ + int i = 0; + + for (i = 0; i < def_list.num; i++) { + if (pid == def_list.control[i].pid) + return def_list.control[i].type; + } + return SET_NONE; +} static int cpu_move_cgroup(pid_t pid, char *path) { @@ -64,8 +131,36 @@ static int load_cpu_config(struct parse_result *result, void *user_data) return RESOURCED_ERROR_NO_DATA; if (!strcmp(result->name, CPU_CONF_PREDEFINE)) { pid = find_pid_from_cmdline(result->value); - if (pid > 0) + if (pid > 0) { + cpu_move_cgroup(pid, CPU_CONTROL_GROUP); + def_list.control[def_list.num].pid = pid; + def_list.control[def_list.num++].type = SET_DEFAUT; + } else { + _E("not found appname = %s", result->value); + } + } else if (!strcmp(result->name, CPU_CONF_BOOTING)) { + pid = find_pid_from_cmdline(result->value); + if (pid > 0) { cpu_move_cgroup(pid, CPU_CONTROL_GROUP); + def_list.control[def_list.num].pid = pid; + def_list.control[def_list.num++].type = SET_BOOTING; + setpriority(PRIO_PROCESS, pid, CPU_CONTROL_PRI); + } + } else if (!strcmp(result->name, CPU_CONF_WRT)) { + pid = find_pid_from_cmdline(result->value); + if (pid > 0) { + cpu_move_cgroup(pid, CPU_CONTROL_GROUP); + def_list.control[def_list.num].pid = pid; + def_list.control[def_list.num++].type = SET_WRT; + setpriority(PRIO_PROCESS, pid, CPU_CONTROL_PRI); + ioprio_set(IOPRIO_WHO_PROCESS, pid, IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT); + } + } else if (!strcmp(result->name, CPU_CONF_LAZY)) { + pid = find_pid_from_cmdline(result->value); + if (pid > 0) { + def_list.control[def_list.num].pid = pid; + def_list.control[def_list.num++].type = SET_LAZY; + } } else if (!strcmp(result->name, "BACKGROUND_CPU_SHARE")) { value = atoi(result->value); if (value) @@ -89,8 +184,13 @@ static int cpu_service_launch(void *data) static int cpu_foreground_state(void *data) { struct proc_status *p_data = (struct proc_status*)data; + int pri; _D("cpu_foreground_state : pid = %d, appname = %s", p_data->pid, p_data->appid); - cpu_move_cgroup(p_data->pid, CPU_DEFAULT_CGROUP); + pri = getpriority(PRIO_PROCESS, p_data->pid); + if (pri == -1 || pri > CPU_DEFAULT_PRI) + setpriority(PRIO_PGRP, p_data->pid, CPU_DEFAULT_PRI); + if (check_predefined(p_data->pid) != SET_DEFAUT) + cpu_move_cgroup(p_data->pid, CPU_DEFAULT_CGROUP); return RESOURCED_ERROR_NONE; } @@ -98,10 +198,65 @@ static int cpu_background_state(void *data) { struct proc_status *p_data = (struct proc_status*)data; _D("cpu_background_state : pid = %d, appname = %s", p_data->pid, p_data->appid); + setpriority(PRIO_PGRP, p_data->pid, CPU_BACKGROUND_PRI); cpu_move_cgroup(p_data->pid, CPU_CONTROL_SERVICE_GROUP); return RESOURCED_ERROR_NONE; } +static int cpu_prelaunch_state(void *data) +{ + struct proc_status *p_data = (struct proc_status*)data; + struct proc_process_info_t *ppi = p_data->ppi; + int i = 0; + GSList *iter = NULL; + if (!cpu_predefined_timer) + return RESOURCED_ERROR_NONE; + if (ppi->type & PROC_WEBAPP) { + for (i = 0; i < def_list.num; i++) { + if (def_list.control[i].type == SET_WRT) { + cpu_move_cgroup(def_list.control[i].pid, CPU_DEFAULT_CGROUP); + setpriority(PRIO_PGRP, def_list.control[i].pid, 0); + ioprio_set(IOPRIO_WHO_PROCESS, def_list.control[i].pid, IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT); + return RESOURCED_ERROR_NONE; + } + } + } else { + gslist_for_each_item(iter, ppi->pids) { + struct pid_info_t *pi = (struct pid_info_t *)(iter->data); + if (pi && pi->type == PROC_TYPE_GUI) { + if (check_predefined(pi->pid) == SET_BOOTING) { + cpu_move_cgroup(p_data->pid, CPU_DEFAULT_CGROUP); + setpriority(PRIO_PGRP, p_data->pid, 0); + return RESOURCED_ERROR_NONE; + } + } + } + } + return RESOURCED_ERROR_NONE; +} + +static Eina_Bool cpu_predefined_cb(void *data) +{ + int i = 0; + + for (i = 0; i < def_list.num; i++) { + if (def_list.control[i].type == SET_LAZY) { + cpu_move_cgroup(def_list.control[i].pid, CPU_CONTROL_GROUP); + } else if (def_list.control[i].type == SET_BOOTING) { + cpu_move_cgroup(def_list.control[i].pid, CPU_DEFAULT_CGROUP); + setpriority(PRIO_PROCESS, def_list.control[i].pid, 0); + } else if (def_list.control[i].type == SET_WRT) { + cpu_move_cgroup(def_list.control[i].pid, CPU_DEFAULT_CGROUP); + setpriority(PRIO_PROCESS, def_list.control[i].pid, 0); + ioprio_set(IOPRIO_WHO_PROCESS, def_list.control[i].pid, IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT); + } + } + ecore_timer_del(cpu_predefined_timer); + cpu_predefined_timer = NULL; + return ECORE_CALLBACK_CANCEL; + +} + static int resourced_cpu_init(void *data) { int ret_code; @@ -113,10 +268,14 @@ static int resourced_cpu_init(void *data) ret_value_msg_if(ret_code < 0, ret_code, "create service cgroup failed\n"); config_parse(CPU_CONF_FILE, load_cpu_config, NULL); + if (def_list.num) + cpu_predefined_timer = + ecore_timer_add(CPU_TIMER_INTERVAL, cpu_predefined_cb, NULL); register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_service_launch); register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_foreground_state); register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_foreground_state); register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_background_state); + register_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, cpu_prelaunch_state); return RESOURCED_ERROR_NONE; } @@ -126,6 +285,7 @@ static int resourced_cpu_finalize(void *data) unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_foreground_state); unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_foreground_state); unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_background_state); + unregister_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, cpu_prelaunch_state); return RESOURCED_ERROR_NONE; } diff --git a/src/cpu/cpu.conf b/src/cpu/cpu.conf index 609c4648..4e9e3861 100644 --- a/src/cpu/cpu.conf +++ b/src/cpu/cpu.conf @@ -1,6 +1,10 @@ [CONTROL] # predefined process list +LAZY_PREDEFINE=net-config PREDEFINE=indicator -PREDEFINE=net-config +BOOTING_PREDEFINE=quickpanel +WRT_PREDEFINE=wrt_launchpad_daemon +BOOTING_PREDEFINE=volume +BOOTING_PREDEFINE=nvitemd BACKGROUND_CPU_SHARE=50 SERVICE_CPU_SHARE=128 diff --git a/src/cpu/logging-cpu.c b/src/cpu/logging-cpu.c index bdb7c1a8..c6ae0784 100644 --- a/src/cpu/logging-cpu.c +++ b/src/cpu/logging-cpu.c @@ -55,7 +55,7 @@ #define CPU_MAX_INTERVAL 20*60 /* 5 min */ #define CPU_INIT_INTERVAL 20*60 /* 3 min */ -#define CPU_FOREGRD_INTERVAL 3*60 /* 1 min */ +#define CPU_FOREGRD_INTERVAL 2 /* 1 min */ #define CPU_BACKGRD_INTERVAL 10*60 /* 2 min */ #define CPU_BACKGRD_OLD_INTERVAL 15*60 /* 5 min */ @@ -64,6 +64,8 @@ struct logging_cpu_info { unsigned long stime; unsigned long last_utime; unsigned long last_stime; + unsigned long last_commit_utime; + unsigned long last_commit_stime; bool last_commited; time_t last_log_time; time_t log_interval; @@ -147,6 +149,8 @@ static int init_cpu_info(void **pl, int pid, int oom, time_t now) info->stime = 0; info->last_utime = 0; info->last_stime = 0; + info->last_commit_utime = 0; + info->last_commit_stime = 0; info->last_log_time = now; info->last_commited = false; @@ -215,13 +219,18 @@ static int write_cpu_info(char *name, struct logging_infos *infos, if (!infos->running && ci->last_commited) return RESOURCED_ERROR_NONE; + if (ci->last_commit_utime == ci->utime && + ci->last_commit_stime == ci->stime) + return RESOURCED_ERROR_NONE; + sd_journal_send("NAME=cpu", "TIME=%ld", ci->last_log_time, "PNAME=%s", name, "UTIME=%ld", ci->utime, "STIME=%ld", ci->stime, NULL); - + ci->last_commit_utime = ci->utime; + ci->last_commit_stime = ci->stime; ci->last_commited = true; return RESOURCED_ERROR_NONE; diff --git a/src/logging/include/logging.h b/src/logging/include/logging.h index e64a2230..7e27a87d 100644 --- a/src/logging/include/logging.h +++ b/src/logging/include/logging.h @@ -42,5 +42,4 @@ struct logging_info_ops { int register_logging_subsystem(const char *name, struct logging_info_ops *ops); int update_commit_interval(const char *name, time_t commit_interval); -int get_pss(pid_t pid, unsigned *pss, unsigned *uss); #endif /*__LOGGING_H__*/ diff --git a/src/logging/logging.c b/src/logging/logging.c index fd81f1c9..166a0513 100644 --- a/src/logging/logging.c +++ b/src/logging/logging.c @@ -54,11 +54,12 @@ #define WRITE_INFO_MAX 10 #define MAX_PROC_LIST 200 -#define WEBPROCESS_NAME "WebProcess" +#define WEBPROCESS_NAME "/usr/bin/WebProcess" +#define WEBPROCESS_NAME_LEN 19 #define MAX_PROC_ITEM 200 #define INC_PROC_ITEM 10 #define COMMIT_INTERVAL 10*60 /* 10 min */ -#define LOGGING_PTIORITY -20 +#define LOGGING_PTIORITY 20 #define SIGNAL_LOGGING_INIT "LoggingInit" #define SIGNAL_LOGGING_GET "LoggingGet" @@ -84,6 +85,8 @@ static pthread_mutex_t logging_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t proc_list_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t logging_cond = PTHREAD_COND_INITIALIZER; +static void broadcast_logging_data_updated_signal(void); + static int init_logging_infos(struct logging_infos *info, const char *key, pid_t pid, int oom, time_t now) { @@ -139,9 +142,9 @@ static void insert_hash_table(char *key, pid_t pid, int oom) struct logging_infos *info; void **stats; char *name; - time_t now; + struct timespec ts; - now = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &ts); name = malloc(strlen(key) + 1); @@ -168,10 +171,10 @@ static void insert_hash_table(char *key, pid_t pid, int oom) } info->stats = stats; - init_logging_infos(info, name, pid, oom, now); + init_logging_infos(info, name, pid, oom, ts.tv_sec); g_hash_table_insert(logging_proc_list, (gpointer) name, (gpointer) info); - update_logging_infos(info, now, true); + update_logging_infos(info, ts.tv_sec, true); return; } @@ -227,6 +230,9 @@ static int write_logging_subsys_info(struct logging_sub_sys *pss, int sindex, write_journal(pss, sindex); pss->last_commit = now; + + broadcast_logging_data_updated_signal(); + return RESOURCED_ERROR_NONE; } @@ -235,16 +241,16 @@ static int write_logging_infos(bool force) { int i; int ret; - time_t now; + struct timespec ts; - now = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &ts); for (i = 0; i < logging_ss_list->len; i++) { struct logging_sub_sys *ss = &g_array_index(logging_ss_list, struct logging_sub_sys, i); - ret = write_logging_subsys_info(ss, i, now, force); + ret = write_logging_subsys_info(ss, i, ts.tv_sec, force); if (ret != RESOURCED_ERROR_NONE) { - _E("write logging at %lu", now); + _E("write logging at %lu", ts.tv_sec); /* not return error, just continue updating */ } } @@ -256,7 +262,7 @@ int register_logging_subsystem(const char*name, struct logging_info_ops *ops) { struct logging_sub_sys ss; char *ss_name; - time_t now; + struct timespec ts; ss_name = malloc(strlen(name)+1); @@ -265,12 +271,12 @@ int register_logging_subsystem(const char*name, struct logging_info_ops *ops) return RESOURCED_ERROR_FAIL; } - now = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &ts); strcpy(ss_name, name); ss.name = ss_name; ss.commit_interval = COMMIT_INTERVAL; - ss.last_commit = now; + ss.last_commit = ts.tv_sec; ss.ops = ops; g_array_append_val(logging_ss_list, ss); @@ -299,21 +305,7 @@ int update_commit_interval(const char *name, time_t commit_interval) static inline int is_webprocess(char *name) { - return !strcmp(name, WEBPROCESS_NAME); -} - -static int rename_webprocess(pid_t pgid, char *name) -{ - char webui_name[PROC_NAME_MAX]; - int ret; - - if ((ret = proc_get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE) - return RESOURCED_ERROR_FAIL; - - strcat(name, "."); - strcat(name, webui_name); - - return RESOURCED_ERROR_NONE; + return !strncmp(name, WEBPROCESS_NAME, WEBPROCESS_NAME_LEN); } static int get_cmdline(pid_t pid, char *cmdline) @@ -335,6 +327,20 @@ static int get_cmdline(pid_t pid, char *cmdline) return RESOURCED_ERROR_NONE; } +static int rename_webprocess(pid_t pgid, char *name) +{ + char webui_name[PROC_NAME_MAX]; + int ret; + + if ((ret = get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE) + return RESOURCED_ERROR_FAIL; + + strcat(name, ":"); + strcat(name, webui_name); + + return RESOURCED_ERROR_NONE; +} + static void insert_proc_list(pid_t pid, pid_t pgid, int oom) { int ret = RESOURCED_ERROR_NONE; @@ -460,14 +466,14 @@ static void update_proc_list(void) { GHashTableIter iter; gpointer key, value; - time_t now; struct logging_infos *infos; + struct timespec ts; int ret; if (need_to_update) update_proc_state(); - now = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &ts); g_hash_table_iter_init(&iter, logging_proc_list); @@ -490,7 +496,7 @@ static void update_proc_list(void) infos = (struct logging_infos *)value; if (infos->running) - update_logging_infos(infos, now, false); + update_logging_infos(infos, ts.tv_sec, false); ret = pthread_mutex_unlock(&proc_list_mutex); if (ret) { _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret); @@ -737,7 +743,6 @@ static void logging_get_edbus_signal_handler(void *data, DBusMessage *msg) } write_logging_infos(true); _D("logging_get_edbus_signal_handler"); - broadcast_logging_data_updated_signal(); } static int logging_init(void) @@ -771,11 +776,11 @@ static int logging_init(void) register_edbus_signal_handler(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_INIT, - (void *)logging_init_booting_done_edbus_signal_handler); + (void *)logging_init_booting_done_edbus_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_GET, - (void *)logging_get_edbus_signal_handler); + (void *)logging_get_edbus_signal_handler, NULL); return RESOURCED_ERROR_NONE; } diff --git a/src/memory/helper.c b/src/memory/helper.c new file mode 100644 index 00000000..614ba9cc --- /dev/null +++ b/src/memory/helper.c @@ -0,0 +1,542 @@ +/* + * resourced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file smap-helper.c + * + * @desc proc/<pid>/smaps file helper functions + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> +#include <linux/limits.h> + +#include <ctype.h> +#include <stddef.h> + +#include <dirent.h> +#include <sys/utsname.h> +#include <stdbool.h> + +#include "trace.h" +#include "logging.h" +#include "file-helper.h" +#include "edbus-handler.h" + +#include "helper.h" + +/* Memory info (/proc/meminfo) */ +#define MEMINFO_PATH "/proc/meminfo" +#define MEMINFO_TOTAL "MemTotal:" +#define MEMINFO_FREE "MemFree:" +#define MEMINFO_AVAILABLE "MemAvailable:" +#define MEMINFO_CACHED "Cached:" +#define KBtoMB(x) ((x)>>10) + +static struct mapinfo *mi; +static struct mapinfo *maps; +static int smaps_initialized; + +bool starts_with(const char *pref, const char *str, const size_t size) +{ + return strncmp(pref, str, size) == 0; +} + +static int read_mapinfo(char **smaps) +{ + char *line; + unsigned tmp; + unsigned read_lines = 0; + int ignore = 0; + static unsigned ignored_lines; + + mi->size = 0; + mi->rss = 0; + mi->pss = 0; + mi->shared_clean = 0; + mi->shared_dirty = 0; + mi->private_clean = 0; + mi->private_dirty = 0; + + while ((line = cgets(smaps)) != NULL) { + tmp = 0; + + /* + * Fast ignore lines, when we know how much + * we can ignore to the end. + */ + if (ignore > 0 && ignored_lines > 0) { + ignore--; + continue; + } + + if (starts_with("Size: ", line, 6)) { + if (sscanf(line, "Size: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->size += tmp; + continue; + } + } else if (starts_with("Rss: ", line, 5)) { + if (sscanf(line, "Rss: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->rss += tmp; + continue; + } + } else if (starts_with("Pss: ", line, 5)) { + if (sscanf(line, "Pss: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->pss += tmp; + continue; + } + } else if (starts_with("Shared_Clean: ", line, 14)) { + if (sscanf(line, "Shared_Clean: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->shared_clean += tmp; + continue; + } + } else if (starts_with("Shared_Dirty: ", line, 14)) { + if (sscanf(line, "Shared_Dirty: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->shared_dirty += tmp; + continue; + } + } else if (starts_with("Private_Clean: ", line, 15)) { + if (sscanf(line, "Private_Clean: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->private_clean += tmp; + continue; + } + } else if (starts_with("Private_Dirty: ", line, 15)) { + if (sscanf(line, "Private_Dirty: %d kB", &tmp) != 1) { + return RESOURCED_ERROR_FAIL; + } else { + mi->private_dirty += tmp; + /* + * We just read last interesting for us field. + * Now we can ignore the rest of current block. + */ + ignore = ignored_lines; + continue; + } + } else { + /* + * This calculates how many lines from the last field read + * we can safety ignore. + * The 'header line' is also counted, later we remove it + * because it is the first one and we don't want to overlap + * later when reading. + * + * The last line in smaps single block starts with 'VmFlags: ' + * when occurred we know the amount of fields that we can ignore + * in smaps block. + * We count that only once per resourced running. (depends on + * kernel version) + * + * This won't work if we want to omit some fields in the middle + * of smaps block. + */ + + read_lines++; /* not handled before, so count */ + + if (ignored_lines == 0) /* make it only once */ + if (starts_with("VmFlags: ", line, 9)) + ignored_lines = read_lines-1; + + continue; /* ignore that line anyways */ + } + } + + return RESOURCED_ERROR_NONE; +} + + +static void init_maps(void) +{ + maps->size = 0; + maps->rss = 0; + maps->pss = 0; + maps->shared_clean = 0; + maps->shared_dirty = 0; + maps->private_clean = 0; + maps->private_dirty = 0; +} + +static int load_maps(int pid) +{ + char *smaps, *start; + char tmp[128]; + + sprintf(tmp, "/proc/%d/smaps", pid); + smaps = cread(tmp); + if (smaps == NULL) + return RESOURCED_ERROR_FAIL; + + start = smaps; + init_maps(); + + read_mapinfo(&smaps); + + maps->size = mi->size; + maps->rss = mi->rss; + maps->pss = mi->pss; + maps->shared_clean = mi->shared_clean; + maps->shared_dirty = mi->shared_dirty; + maps->private_clean = mi->private_clean; + maps->private_dirty = mi->private_dirty; + + _D("load_maps: %d %d %d %d %d", maps->size, maps->pss, + maps->rss, maps->shared_dirty, maps->private_dirty); + + if (start) + free(start); + + return RESOURCED_ERROR_NONE; +} + + +static int allocate_memory(void) +{ + if (smaps_initialized > 0) { + _D("smaps helper already initialized"); + return RESOURCED_ERROR_NONE; + } + + maps = (struct mapinfo *)malloc(sizeof(struct mapinfo)); + + if (!maps) { + _E("fail to allocate mapinfo\n"); + return RESOURCED_ERROR_FAIL; + } + + mi = malloc(sizeof(struct mapinfo)); + if (mi == NULL) { + _E("malloc failed for mapinfo"); + free(maps); + return RESOURCED_ERROR_FAIL; + } + + smaps_initialized++; + + return RESOURCED_ERROR_NONE; +} + +int smaps_helper_get_meminfo(pid_t pid, struct mapinfo **meminfo) +{ + int ret; + + ret = load_maps(pid); + if (ret != RESOURCED_ERROR_NONE) + init_maps(); + else + *meminfo = maps; + return ret; +} + +static int load_statm(int pid) +{ + FILE *fp; + char tmp[128]; + + sprintf(tmp, "/proc/%d/statm", pid); + fp = fopen(tmp, "r"); + if (fp == NULL) + return RESOURCED_ERROR_FAIL; + + if (fscanf(fp, "%d %d", &mi->size, &mi->rss) < 1) { + fclose(fp); + return RESOURCED_ERROR_FAIL; + } + fclose(fp); + + /* Convert from pages to Kb */ + maps->size = mi->size*4; + maps->rss = mi->rss*4; + + _D("load_statm: %d %d", maps->size, maps->rss); + + return RESOURCED_ERROR_NONE; +} + +int smaps_helper_get_vmsize(pid_t pid, unsigned *vmsize, unsigned *vmrss) +{ + int ret; + + ret = load_maps(pid); + if (ret != RESOURCED_ERROR_NONE) { + *vmsize = 0; + *vmrss = 0; + } else { + *vmsize = maps->size; + *vmrss = maps->rss; + } + + return ret; +} + +int statm_helper_get_vmsize(pid_t pid, unsigned *vmsize, unsigned *vmrss) +{ + int ret; + + ret = load_statm(pid); + if (ret != RESOURCED_ERROR_NONE) { + *vmsize = 0; + *vmrss = 0; + } else { + *vmsize = maps->size; + *vmrss = maps->rss; + } + + return ret; +} + +int smaps_helper_get_shared(pid_t pid, unsigned *shared_clean, + unsigned *shared_dirty) +{ + int ret; + + ret = load_maps(pid); + if (ret != RESOURCED_ERROR_NONE) { + *shared_clean = 0; + *shared_dirty = 0; + } else { + *shared_clean = maps->shared_clean; + *shared_dirty = maps->private_dirty; + } + + return ret; +} + +int smaps_helper_get_pss(pid_t pid, unsigned *pss, unsigned *uss) +{ + int ret; + + ret = load_maps(pid); + if (ret != RESOURCED_ERROR_NONE) { + *pss = 0; + *uss = 0; + } else { + *pss = maps->pss; + *uss = maps->private_clean + maps->private_dirty; + } + + return ret; +} + +int smaps_helper_init(void) +{ + int ret; + + ret = allocate_memory(); + + if (ret != RESOURCED_ERROR_NONE) { + _E("allocate structures failed"); + return RESOURCED_ERROR_FAIL; + } + + smaps_initialized--; + return RESOURCED_ERROR_NONE; +} + +void smaps_helper_free(void) +{ + free(maps); + free(mi); +} + +struct memory_info { + unsigned int total; + unsigned int free; + unsigned int available; + unsigned int cached; +}; + +static int read_mem_info(char *buf, char *type, unsigned int *val) +{ + char *idx; + + if (!type || !buf || !val) + return -EINVAL; + + idx = strstr(buf, type); + if (!idx) + return -ENOENT; + + idx += strlen(type); + + while (*idx < '0' || *idx > '9') + idx++; + + *val = strtoul(idx, NULL, 10); + + return 0; +} + +static unsigned int get_mem_info(struct memory_info *info) +{ + char buf[PATH_MAX]; + size_t len; + FILE *fp; + int ret; + + if (!info) + return -EINVAL; + + fp = fopen(MEMINFO_PATH, "r"); + if (!fp) { + ret = -errno; + _E("%s open failed(errno:%d)", MEMINFO_PATH, ret); + return ret; + } + + len = sizeof(buf); + while (fgets(buf, len, fp) != NULL) { + if (read_mem_info(buf, MEMINFO_TOTAL, &(info->total)) == 0) + continue; + if (read_mem_info(buf, MEMINFO_FREE, &(info->free)) == 0) + continue; + if (read_mem_info(buf, MEMINFO_AVAILABLE, &(info->available)) == 0) + continue; + if (read_mem_info(buf, MEMINFO_CACHED, &(info->cached)) == 0) + continue; + } + + fclose(fp); + + if (info->available == 0) + info->available = info->cached + info->free; + + return 0; +} + +unsigned int get_available(void) +{ + int ret; + struct memory_info info = {0,}; + int available = 0; + + ret = get_mem_info(&info); + if (ret < 0) { + _E("Failed to get mem info (%d)", ret); + return available; + } + + available = info.available; + + return KBtoMB(available); +} + +unsigned int get_mem_usage(void) +{ + int ret; + struct memory_info info = {0,}; + int usage; + + ret = get_mem_info(&info); + if (ret < 0) { + _E("Failed to get mem info (%d)", ret); + return ret; + } + + usage = info.total - info.available; + + return KBtoMB(usage); +} + +#include <bundle.h> +#include <bundle_internal.h> + +#define EVENT_SYSTEM_PATH "/tizen/system/event" +#define EVENT_SYSTEM_IFACE "tizen.system.event" +#define EVENT_SYSTEM_SIGNAL "low_memory" + +#define EVT_KEY_LOW_MEMORY "low_memory" +#define EVT_VAL_MEMORY_NORMAL "normal" +#define EVT_VAL_MEMORY_SOFT_WARNING "soft_warning" +#define EVT_VAL_MEMORY_HARD_WARNING "hard_warning" + +#define BUF_MAX 128 + +void memory_level_send_system_event(int lv) +{ + bundle *b; + bundle_raw *raw = NULL; + int raw_len; + const char *str; + char *param[3]; + char trusted[BUF_MAX]; + char len[BUF_MAX]; + int ret; + + switch (lv) { + case MEMORY_LEVEL_NORMAL: + str = EVT_VAL_MEMORY_NORMAL; + break; + case MEMORY_LEVEL_LOW: + str = EVT_VAL_MEMORY_SOFT_WARNING; + break; + case MEMORY_LEVEL_CRITICAL: + str = EVT_VAL_MEMORY_HARD_WARNING; + break; + default: + _E("Invalid state"); + return; + } + + _I("Send event system signal (%s)", str); + + b = bundle_create(); + if (!b) { + _E("Failed to create bundle"); + return; + } + + bundle_add_str(b, EVT_KEY_LOW_MEMORY, str); + bundle_encode(b, &raw, &raw_len); + + snprintf(trusted, sizeof(trusted), "%d", 0); /* 0 == FALSE */ + param[0] = trusted; + snprintf(len, sizeof(len), "%d", raw_len); + param[1] = len; + param[2] = (char *)raw; + + ret = broadcast_edbus_signal_str( + EVENT_SYSTEM_PATH, + EVENT_SYSTEM_IFACE, + EVENT_SYSTEM_SIGNAL, + "bus", param); + if (ret < 0) + _E("Failed to send signal of memory state to event-system"); + + bundle_free_encoded_rawdata(&raw); + bundle_free(b); +} diff --git a/src/memory/helper.h b/src/memory/helper.h new file mode 100644 index 00000000..ef26299c --- /dev/null +++ b/src/memory/helper.h @@ -0,0 +1,58 @@ +/* + * resourced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file smaps-helper.h + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#ifndef SRC_MEMORY_SMAPS_HELPER_H_ +#define SRC_MEMORY_SMAPS_HELPER_H_ + +struct mapinfo { + unsigned size; + unsigned rss; + unsigned pss; + unsigned shared_clean; + unsigned shared_dirty; + unsigned private_clean; + unsigned private_dirty; +}; + +int smaps_helper_get_meminfo(pid_t pid, struct mapinfo **meminfo); +int smaps_helper_get_pss(pid_t pid, unsigned *pss, unsigned *uss); +int smaps_helper_get_shared(pid_t pid, unsigned *shared_clean, unsigned *shared_dirty); +int smaps_helper_get_vmsize(pid_t pid, unsigned *vmsize, unsigned *vmrss); +int statm_helper_get_vmsize(pid_t pid, unsigned *vmsize, unsigned *vmrss); +int smaps_helper_init(void); +void smaps_helper_free(void); + +unsigned int get_available(void); +unsigned int get_mem_usage(void); + +enum memory_level { + MEMORY_LEVEL_NORMAL, + MEMORY_LEVEL_LOW, + MEMORY_LEVEL_CRITICAL, +}; + +void memory_level_send_system_event(int lv); + +#endif /* SRC_MEMORY_SMAPS_HELPER_H_ */ diff --git a/src/memory/logging-memory.c b/src/memory/logging-memory.c index 532d6712..8fca2726 100644 --- a/src/memory/logging-memory.c +++ b/src/memory/logging-memory.c @@ -49,6 +49,7 @@ #include "macro.h" #include "proc-process.h" #include "logging.h" +#include "smaps-helper.h" #define MEM_NAME "memory" #define MEM_COMMIT_INTERVAL 30*60 /* 30 min */ @@ -71,165 +72,6 @@ struct logging_memory_info { pid_t current_pid; }; -struct mapinfo { - unsigned size; - unsigned rss; - unsigned pss; - unsigned shared_clean; - unsigned shared_dirty; - unsigned private_clean; - unsigned private_dirty; -}; - -static int ignore_smaps_field; -static struct mapinfo *mi; -static struct mapinfo *maps; - -static void check_kernel_version(void) -{ - struct utsname buf; - int ret; - - ret = uname(&buf); - - if (ret) - return; - - if (buf.release[0] == '3') { - char *pch; - char str[3]; - int sub_version; - pch = strstr(buf.release, "."); - strncpy(str, pch+1, 2); - sub_version = atoi(str); - - if (sub_version >= 10) - ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages, - Swap, KernelPageSize, MMUPageSize, - Locked, VmFlags */ - - else - ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages, - Swap, KernelPageSize, MMUPageSize, - Locked */ - } else { - ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize, - MMUPageSize */ - } -} - - -/* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /android/lib/libcomposer.so - * 012345678901234567890123456789012345678901234567890123456789 - * 0 1 2 3 4 5 - */ - -static int read_mapinfo(char** smaps, int rest_line) -{ - char* line; - int len; - - if ((line = cgets(smaps)) == 0) - return RESOURCED_ERROR_FAIL; - - len = strlen(line); - if (len < 1) { - _E("line is less than 1"); - return RESOURCED_ERROR_FAIL; - } - - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Size: %d kB", &mi->size) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Rss: %d kB", &mi->rss) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Pss: %d kB", &mi->pss) == 1) - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1) - goto oops; - if ((line = cgets(smaps)) == 0) - goto oops; - if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1) - goto oops; - - while (rest_line-- && cgets(smaps)) - ; - - return RESOURCED_ERROR_NONE; - oops: - _E("mi get error\n"); - return RESOURCED_ERROR_FAIL; -} - -static void init_maps() -{ - maps->size = 0; - maps->rss = 0; - maps->pss = 0; - maps->shared_clean = 0; - maps->shared_dirty = 0; - maps->private_clean = 0; - maps->private_dirty = 0; -} - -static int load_maps(int pid) -{ - char* smaps, *start; - char tmp[128]; - - sprintf(tmp, "/proc/%d/smaps", pid); - smaps = cread(tmp); - if (smaps == NULL) - return RESOURCED_ERROR_FAIL; - - start = smaps; - init_maps(); - - while (read_mapinfo(&smaps, ignore_smaps_field) - == RESOURCED_ERROR_NONE) { - maps->size += mi->size; - maps->rss += mi->rss; - maps->pss += mi->pss; - maps->shared_clean += mi->shared_clean; - maps->shared_dirty += mi->shared_dirty; - maps->private_clean += mi->private_clean; - maps->private_dirty += mi->private_dirty; - } - - if(start) - free(start); - return RESOURCED_ERROR_NONE; -} - -int get_pss(pid_t pid, unsigned *pss, unsigned *uss) -{ - int ret; - ret = load_maps(pid); - if (ret != RESOURCED_ERROR_NONE) { - *pss = 0; - *uss = 0; - } else { - *pss = maps->pss; - *uss = maps->private_clean + maps->private_dirty; - } - - return ret; -} - static void update_log_interval(struct logging_memory_info *loginfo, int oom, int always) { @@ -296,7 +138,7 @@ static int update_memory_info(void *pl, pid_t pid, int oom, if (now < loginfo->last_log_time + loginfo->log_interval) return ret; - ret = get_pss(pid, &pss, &uss); + ret = smaps_helper_get_pss(pid, &pss, &uss); if (ret != RESOURCED_ERROR_NONE) return ret; @@ -346,52 +188,25 @@ static struct logging_info_ops memory_info_ops = { .init = init_memory_info, }; -static int allocate_memory(void) -{ - maps = (struct mapinfo *)malloc(sizeof(struct mapinfo)); - - if (!maps) { - _E("fail to allocate mapinfo\n"); - return RESOURCED_ERROR_FAIL; - } - - mi = malloc(sizeof(struct mapinfo)); - if (mi == NULL) { - _E("malloc failed for mapinfo"); - free(maps); - return RESOURCED_ERROR_FAIL; - } - return RESOURCED_ERROR_NONE; -} - -static void free_memory(void) -{ - free(maps); - free(mi); -} - static int logging_memory_init(void *data) { int ret; - check_kernel_version(); - - ret = allocate_memory(); - + ret = smaps_helper_init(); if (ret != RESOURCED_ERROR_NONE) { - _E("allocate structures failed"); - return RESOURCED_ERROR_FAIL; + _E("smaps helper failed"); + return RESOURCED_ERROR_FAIL; } ret = register_logging_subsystem(MEM_NAME, &memory_info_ops); if(ret != RESOURCED_ERROR_NONE) { _E("register logging subsystem failed"); - free_memory(); + smaps_helper_free(); return RESOURCED_ERROR_FAIL; } ret = update_commit_interval(MEM_NAME, MEM_COMMIT_INTERVAL); if(ret != RESOURCED_ERROR_NONE) { _E("update commit interval logging subsystem failed"); - free_memory(); + smaps_helper_free(); return RESOURCED_ERROR_FAIL; } @@ -402,7 +217,7 @@ static int logging_memory_init(void *data) static int logging_memory_exit(void *data) { _D("logging memory finalize"); - free_memory(); + smaps_helper_free(); return RESOURCED_ERROR_NONE; } diff --git a/src/memory/lowmem-dbus.c b/src/memory/lowmem-dbus.c index d2626257..4fa5828a 100644 --- a/src/memory/lowmem-dbus.c +++ b/src/memory/lowmem-dbus.c @@ -32,6 +32,7 @@ #include "edbus-handler.h" #include "resourced.h" #include "macro.h" +#include "memcontrol.h" #define SIGNAL_NAME_OOM_SET_THRESHOLD "SetThreshold" #define SIGNAL_NAME_OOM_SET_LEAVE_THRESHOLD "SetLeaveThreshold" @@ -59,7 +60,7 @@ static void lowmem_dbus_oom_set_threshold(void *data, DBusMessage *msg) return; } - set_threshold(level, thres); + lowmem_memcg_set_threshold(MEMCG_MEMORY, level, thres); } static void lowmem_dbus_oom_set_leave_threshold(void *data, DBusMessage *msg) @@ -84,7 +85,7 @@ static void lowmem_dbus_oom_set_leave_threshold(void *data, DBusMessage *msg) return; } - set_leave_threshold(thres); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, thres); } static void lowmem_dbus_oom_trigger(void *data, DBusMessage *msg) @@ -107,22 +108,22 @@ static void lowmem_dbus_oom_trigger(void *data, DBusMessage *msg) if (launching) flags |= OOM_NOMEMORY_CHECK; - change_memory_state(MEMNOTIFY_LOW, 1); - lowmem_oom_killer_cb(MEMCG_MEMORY, flags); + change_memory_state(LOWMEM_LOW, 1); + lowmem_memory_oom_killer(flags); _D("flags = %d", flags); - change_memory_state(MEMNOTIFY_NORMAL, 0); + change_memory_state(LOWMEM_NORMAL, 0); } void lowmem_dbus_init(void) { register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM, SIGNAL_NAME_OOM_SET_THRESHOLD, - lowmem_dbus_oom_set_threshold); + lowmem_dbus_oom_set_threshold, NULL); register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM, SIGNAL_NAME_OOM_SET_LEAVE_THRESHOLD, - lowmem_dbus_oom_set_leave_threshold); + lowmem_dbus_oom_set_leave_threshold, NULL); register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM, SIGNAL_NAME_OOM_TRIGGER, - lowmem_dbus_oom_trigger); + lowmem_dbus_oom_trigger, NULL); } diff --git a/src/memory/lowmem-handler.c b/src/memory/lowmem-handler.c index 6dd6466c..200b6975 100644 --- a/src/memory/lowmem-handler.c +++ b/src/memory/lowmem-handler.c @@ -26,6 +26,7 @@ */ #include <stdio.h> +#include <stdlib.h> #include <fcntl.h> #include <assert.h> #include <limits.h> @@ -46,88 +47,32 @@ #include "cgroup.h" #include "proc-main.h" #include "lowmem-handler.h" -#include "proc-process.h" #include "swap-common.h" +#include "proc-process.h" #include "lowmem-common.h" #include "resourced.h" #include "macro.h" #include "module.h" #include "notifier.h" +#include "helper.h" -enum { - MEMGC_OOM_NORMAL, - MEMGC_OOM_SOFTSWAP, - MEMGC_OOM_WARNING, - MEMGC_OOM_HIGH, - MEMGC_OOM_CRITICAL, -}; - -enum { - MEMGC_GROUP_FOREGROUND, - MEMGC_GROUP_BACKGROUND, -}; - -enum { - MEM_SWAP_OFF, - MEM_SWAP_ENABLE, - MEM_SWAP_DECREASE, - MEM_SWAP_INCREASE, -}; - -#define MEM_SOFTSWAP_ENABLE 1 -#define MEMCG_GROUP_MAX 2 - -#define MEMINFO_PATH "/proc/meminfo" -#define MEMCG_PATH "/sys/fs/cgroup/memory" -#define VICTIM_TASK "/sys/class/lowmemnotify/victims" -#define SET_LEAVE_THRESHOLD "/sys/class/lowmemnotify/leave_threshold" -#define SET_CGROUP_LEAVE_THRESHOLD "/sys/class/lowmemnotify/cgroup_leave_threshold" -#define SET_THRESHOLD_LV1 "/sys/class/lowmemnotify/threshold_level1" -#define SET_THRESHOLD_LV2 "/sys/class/lowmemnotify/threshold_level2" -#define SET_THRESHOLD_RECLAIM "/sys/class/lowmemnotify/threshold_reclaim" - -#define MEMPS_LOG_FILE "/var/log/memps" +#define DEV_MEMNOTIFY "/dev/memnotify" +#define VICTIM_TASK "/sys/class/memnotify/victims" +#define SET_THRESHOLD_LV1 "/sys/class/memnotify/threshold_lv1" +#define SET_THRESHOLD_LV2 "/sys/class/memnotify/threshold_lv2" -#define DELETE_SM "sh -c "PREFIX"/bin/delete.sm" +#define MEMPS_LOG_FILE "/var/log/memps" #define MEMPS_EXEC_PATH "usr/bin/memps" - -#define _SYS_RES_CLEANUP "RES_CLEANUP" - -#define BtoMB(x) ((x) / 1024 / 1024) -#define MBtoB(x) (x<<20) - -#define MEMCG_FOREGROUND_LIMIT_RATIO 0.6 -#define MEMCG_BACKGROUND_LIMIT_RATIO 0.7 - -#define MEMCG_FOREGROUND_MIN_LIMIT MBtoB(400) -#define MEMCG_BACKGROUND_MIN_LIMIT UINT_MAX - -/* threshold lv 1 : wakeup softswapd */ -#define MEMCG_TRHES_SOFTSWAP_RATIO 0.75 - -/* threshold lv 2 : lowmem warning */ -#define MEMCG_THRES_WARNING_RATIO 0.92 - -/* threshold lv 3 : victim kill */ -#define MEMCG_THRES_OOM_RATIO 0.96 - -/* leave threshold */ -#define MEMCG_OOMLEAVE_RATIO 0.88 +#define BtoMB(x) ((x)>>20) +#define KBtoMB(x) ((x)>>10) +#define MBtoB(x) ((x)<<20) +#define MBtoKB(x) ((x)<<10) #define MEMNOTIFY_NORMAL 0x0000 -#define MEMNOTIFY_RECLAIM 0xecae #define MEMNOTIFY_LOW 0xfaac #define MEMNOTIFY_CRITICAL 0xdead -/* define threshold limit */ -#define MAX_OOM_THRES 0x04600000 /* 70M */ -#define MIN_OOM_THRES 0x03000000 /* 48M */ -#define MAX_WARN_THRES 0x07800000 /* 120M */ -#define MAX_LEAVE_THRES 0x0B400000 /* 180M */ -#define MIN_OOM_WARN_GAP 0x01400000 /* 30M */ - -#define MEM_THRESHOLD_RECLAIM 300 #define MEM_THRESHOLD_LV1 180 #define MEM_THRESHOLD_LV2 160 #define MEM_LEAVE_THRESHOLD 200 @@ -142,38 +87,15 @@ static int cur_mem_state = MEMNOTIFY_NORMAL; static Ecore_Timer *oom_check_timer; #define OOM_TIMER_INTERVAL 3 #define OOM_MULTIKILL_WAIT (1000*1000) -#define OOM_CHECK_PROC_WAIT (2000*1000) - -unsigned int oom_delete_sm_time; /* low memory action function */ static int memory_low_act(void *ad); static int memory_oom_act(void *ad); static int memory_normal_act(void *ad); -static int memory_reclaim_act(void *ad); - - -/* low memory action function for cgroup */ -static int memory_cgroup_oom_act(int memcg_index); static int lowmem_fd_start(); static int lowmem_fd_stop(int fd); -struct memcg_class { - unsigned int event_fd; - unsigned int min_limit; - float limit_ratio; - unsigned int oomlevel; - unsigned int oomalert; - unsigned int oomleave; - char *cgroup_name; - unsigned int total_limit; - unsigned int thres_lv1; - unsigned int thres_lv2; - unsigned int thres_lv3; - unsigned int thres_leave; -}; - struct lowmem_process_entry { unsigned cur_mem_state; unsigned new_mem_state; @@ -181,82 +103,45 @@ struct lowmem_process_entry { }; static struct lowmem_process_entry lpe[] = { - {MEMNOTIFY_NORMAL, MEMNOTIFY_RECLAIM, memory_reclaim_act}, - {MEMNOTIFY_NORMAL, MEMNOTIFY_LOW, memory_low_act}, - {MEMNOTIFY_NORMAL, MEMNOTIFY_CRITICAL, memory_oom_act}, - {MEMNOTIFY_RECLAIM, MEMNOTIFY_LOW, memory_low_act}, - {MEMNOTIFY_RECLAIM, MEMNOTIFY_CRITICAL, memory_oom_act}, - {MEMNOTIFY_LOW, MEMNOTIFY_CRITICAL, memory_oom_act}, + {MEMNOTIFY_NORMAL, MEMNOTIFY_LOW, memory_low_act}, + {MEMNOTIFY_NORMAL, MEMNOTIFY_CRITICAL, memory_oom_act}, + {MEMNOTIFY_LOW, MEMNOTIFY_CRITICAL, memory_oom_act}, + {MEMNOTIFY_LOW, MEMNOTIFY_NORMAL, memory_normal_act}, {MEMNOTIFY_CRITICAL, MEMNOTIFY_CRITICAL, memory_oom_act}, - {MEMNOTIFY_LOW, MEMNOTIFY_RECLAIM, memory_reclaim_act}, - {MEMNOTIFY_LOW, MEMNOTIFY_NORMAL, memory_normal_act}, {MEMNOTIFY_CRITICAL, MEMNOTIFY_NORMAL, memory_normal_act}, - {MEMNOTIFY_CRITICAL, MEMNOTIFY_RECLAIM, memory_reclaim_act}, - {MEMNOTIFY_RECLAIM, MEMNOTIFY_NORMAL, memory_normal_act}, -}; - -static struct memcg_class memcg_class[MEMCG_GROUP_MAX] = { - {0, MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, 0, 0, 0, "foreground", - 0, 0, 0, 0, 0}, - {0, MEMCG_BACKGROUND_MIN_LIMIT, MEMCG_BACKGROUND_LIMIT_RATIO, 0, 0, 0, "background", - 0, 0, 0, 0, 0}, }; static const struct module_ops memory_modules_ops; static const struct module_ops *lowmem_ops; -unsigned int get_available(void) +void lowmem_dynamic_process_killer(int type) { - char buf[PATH_MAX]; - FILE *fp; - char *idx; - unsigned int free = 0, cached = 0; - unsigned int available = 0; - - fp = fopen(MEMINFO_PATH, "r"); - if (!fp) { - _E("%s open failed, %d", buf, fp); - return available; - } - - while (fgets(buf, PATH_MAX, fp) != NULL) { - if ((idx = strstr(buf, "MemFree:"))) { - idx += strlen("MemFree:"); - while (*idx < '0' || *idx > '9') - idx++; - free = atoi(idx); - } else if ((idx = strstr(buf, "MemAvailable:"))) { - idx += strlen("MemAvailable:"); - while (*idx < '0' || *idx > '9') - idx++; - available = atoi(idx); - break; - } else if((idx = strstr(buf, "Cached:"))) { - idx += strlen("Cached:"); - while (*idx < '0' || *idx > '9') - idx++; - cached = atoi(idx); - break; - } - } - - if (available == 0) - available = free + cached; - available >>= 10; - fclose(fp); - - return available; + /* This function is not supported */ } -void lowmem_dynamic_process_killer(int type) +static int convert_memory_state_type(int state) { - /* This function is not supported */ + switch (state) { + case LOWMEM_NORMAL: + return MEMNOTIFY_NORMAL; + case LOWMEM_LOW: + return MEMNOTIFY_LOW; + case LOWMEM_MEDIUM: + return MEMNOTIFY_CRITICAL; + default: + _E("Invalid state (%d)", state); + return -EINVAL; + } } void change_memory_state(int state, int force) { int mem_state; + state = convert_memory_state_type(state); + if (state < 0) + return; + if (force) { mem_state = state; } else { @@ -268,9 +153,6 @@ void change_memory_state(int state, int force) case MEMNOTIFY_NORMAL: memory_normal_act(NULL); break; - case MEMNOTIFY_RECLAIM: - memory_reclaim_act(NULL); - break; case MEMNOTIFY_LOW: memory_low_act(NULL); break; @@ -278,144 +160,9 @@ void change_memory_state(int state, int force) memory_oom_act(NULL); break; default: - assert(0); - } -} - -static unsigned int _get_total_memory(void) -{ - char buf[PATH_MAX]; - FILE *fp; - char *idx; - unsigned int total = 0; - - fp = fopen(MEMINFO_PATH, "r"); - while (fgets(buf, PATH_MAX, fp) != NULL) { - if ((idx = strstr(buf, "MemTotal:"))) { - idx += strlen("MemTotal:"); - while (*idx < '0' || *idx > '9') - idx++; - total = atoi(idx); - total *= 1024; - break; - } - } - fclose(fp); - return total; -} - -static void _calc_threshold(int type, int limit) -{ - unsigned int val, check; - - /* calculate theshold lv3 */ - val = (unsigned int)(memcg_class[type].total_limit* - (float)MEMCG_THRES_OOM_RATIO); - - /* check MIN & MAX value about threshold lv3*/ - if (limit - val > MAX_OOM_THRES) - val = (unsigned int)(limit - MAX_OOM_THRES); - else if (limit - val < MIN_OOM_THRES) - val = (unsigned int)(limit - MIN_OOM_THRES); - - /* set threshold lv3 */ - memcg_class[type].thres_lv3 = val; - - /* calculate threshold lv2 */ - val = (unsigned int)(memcg_class[type].total_limit* - (float)MEMCG_THRES_WARNING_RATIO); - - check = memcg_class[type].thres_lv3; - - /* check MIN & MAX value about threshold lv2*/ - if (check - val < MIN_OOM_WARN_GAP) - val = (unsigned int)(check - MIN_OOM_WARN_GAP); - else if (limit - val > MAX_WARN_THRES) - val = (unsigned int)(limit - MAX_WARN_THRES); - - /* set threshold lv2 */ - memcg_class[type].thres_lv2 = val; - - /* calculate threshold lv1 */ - val = (unsigned int)(memcg_class[type].total_limit* - (float)MEMCG_TRHES_SOFTSWAP_RATIO); - - /* check MIN value about threshold lv1*/ - check = memcg_class[type].thres_lv2; - - if (check - val < MIN_OOM_WARN_GAP) - val = (unsigned int)(check - MIN_OOM_WARN_GAP); - - memcg_class[type].thres_lv1 = val; - - /* set leave threshold */ - val = (unsigned int)(memcg_class[type].total_limit* - (float)MEMCG_OOMLEAVE_RATIO); - - check = memcg_class[type].thres_lv1; - - /* check MIN & MAX value about leave threshold */ - if (check - val < MIN_OOM_WARN_GAP) - val = (unsigned int)(check - MIN_OOM_WARN_GAP); - else if (limit - val > MAX_LEAVE_THRES) - val = (unsigned int)(limit - MAX_WARN_THRES); - - memcg_class[type].oomleave = val; -} - -static unsigned int get_mem_usage(int idx) -{ - FILE *f; - char buf[LOWMEM_PATH_MAX] = {0,}; - unsigned int usage; - - sprintf(buf, "%s/%s/memory.usage_in_bytes", - MEMCG_PATH, memcg_class[idx].cgroup_name); - - f = fopen(buf, "r"); - if (!f) { - _E("%s open failed, %d", buf, f); - return RESOURCED_ERROR_FAIL; - } - if (fgets(buf, 32, f) == NULL) { - _E("fgets failed\n"); - fclose(f); - return RESOURCED_ERROR_FAIL; - } - usage = atoi(buf); - fclose(f); - - return usage; -} - -static int get_current_oom(int idx) -{ - FILE *f; - char buf[LOWMEM_PATH_MAX] = {0,}; - char *oom; - unsigned int level; - - sprintf(buf, "%s/%s/memory.oom_usr_control", - MEMCG_PATH, memcg_class[idx].cgroup_name); - - f = fopen(buf, "r"); - if (!f) { - _E("%s open failed, %d", buf, f); - return RESOURCED_ERROR_FAIL; - } - if (fgets(buf, 32, f) == NULL) { - _E("fgets failed\n"); - fclose(f); - return RESOURCED_ERROR_FAIL; + _E("Invalid mem state (%d)", mem_state); + return; } - oom = strstr(buf, "oom_usr_control"); - oom += strlen("oom_usr_control"); - while (*oom < '0' || *oom > '9') - oom++; - level = atoi(oom); - fclose(f); - _D("get_current_oom : %d", level); - return level; } static int remove_shm(void) @@ -445,14 +192,7 @@ static int remove_shm(void) static void print_mem_state(void) { - unsigned int usage, i; - - for (i = 0; i < MEMCG_GROUP_MAX; i++) { - usage = get_mem_usage(i); - _I("[MEM STATE] memcg : %s, usage %d oom level : %d", - memcg_class[i].cgroup_name, usage, - memcg_class[i].oomlevel); - } + _I("[MEM STATE] usage (%d)", get_mem_usage()); } static void make_memps_log(char *file, pid_t pid, char *victim_name) @@ -492,26 +232,6 @@ static void make_memps_log(char *file, pid_t pid, char *victim_name) } } -static int lowmem_check_current_state(int memcg_index, - int total_size, int oom_usage) -{ - unsigned int usage, oomleave, check = 0; - - oomleave = memcg_class[memcg_index].oomleave; - usage = get_mem_usage(memcg_index); - if (usage < oomleave) { - _D("%s : usage : %d, oomleave : %d", - __func__, usage, oomleave); - check++; - } - if (oom_usage - total_size < oomleave) { - _D("%s : oom_usage : %d, total size : %d, oomleave : %d", - __func__, oom_usage, total_size, oomleave); - check++; - } - return check; -} - static int lowmem_get_victim_pid(int *pid_arry, unsigned int* pid_size) { int count, num_pid = 0; @@ -561,46 +281,9 @@ out: } -static int lowmem_set_cgroup_leave_threshold(unsigned int value) -{ - FILE *f; - f = fopen(SET_CGROUP_LEAVE_THRESHOLD, "w"); - - if (!f) { - _E("Fail to file open"); - return RESOURCED_ERROR_FAIL; - } - fprintf(f, "%d", value); - fclose(f); - return 0; -} - static int lowmem_set_threshold(void) { FILE *f; - unsigned int val, total; - - f = fopen(SET_THRESHOLD_RECLAIM, "w"); - - if (!f) { - _E("Fail to file open : current kernel can't support swap cgroup"); - return RESOURCED_ERROR_FAIL; - } - - /* set threshold reclaim */ - total = _get_total_memory(); - - /* - * check total memory because total memory is over 1GiB, - * we want to start reclaim under 300 MiB remained memory. - * But, we check condition 700MiB because reserved memory. - */ - if (total > MBtoB(700)) - val = MEM_THRESHOLD_RECLAIM; - else - val = MEM_THRESHOLD_RECLAIM >> 1; - fprintf(f, "%d", val); - fclose(f); /* set threshold level1 */ f = fopen(SET_THRESHOLD_LV1, "w"); @@ -622,15 +305,6 @@ static int lowmem_set_threshold(void) fprintf(f, "%d", MEM_THRESHOLD_LV2); fclose(f); - /* set leave threshold */ - f = fopen(SET_LEAVE_THRESHOLD, "w"); - - if (!f) { - _E("Fail to file open"); - return RESOURCED_ERROR_FAIL; - } - fprintf(f, "%d", MEM_LEAVE_THRESHOLD); - fclose(f); return 0; } @@ -713,85 +387,11 @@ void *_lowmem_oom_killer_cb(void *data) return NULL; } -int lowmem_oom_killer_cb(int memcg_idx, int flags) -{ - int memcg_index = memcg_idx; - _lowmem_oom_killer_cb((void *)&memcg_index); - return 0; -} - -static void lowmem_cgroup_oom_killer(int memcg_index) -{ - int pid, ret, oom_score_adj, count, i; - char appname[PATH_MAX]; - int pid_array[32]; - unsigned int pid_size[32]; - unsigned int total_size = 0, oom_usage = 0; - - oom_usage = get_mem_usage(memcg_index); - /* get multiple victims from kernel */ - count = lowmem_get_victim_pid((int *)pid_array, - (unsigned int *)pid_size); - - if (count < 0) { - _E("get victim was failed"); - return; - } - - for (i = 0; i < count; i++) { - pid = pid_array[i]; - - if (pid <= 0) - continue; - _D("oom total memory size : %d", total_size); - ret = proc_get_cmdline(pid, appname); - if (ret != 0) { - _E("invalid pid(%d) was selected", pid); - continue; - } - if (!strcmp("memps", appname)) { - _E("memps(%d) was selected, skip it", pid); - continue; - } - if (!strcmp("crash-worker", appname)) { - _E("crash-worker(%d) was selected, skip it", pid); - continue; - } - if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) { - _D("pid(%d) was already terminated", pid); - continue; - } - - /* check current memory status */ - if (lowmem_check_current_state(memcg_index, total_size, - oom_usage) > 0) - return; - - /* make memps log for killing application firstly */ - if (i==0) - make_memps_log(MEMPS_LOG_FILE, pid, appname); - - proc_remove_process_list(pid); - kill(pid, SIGTERM); - - total_size += pid_size[i]; - _I("we killed, lowmem lv2 = %d (%s) oom = %d\n", - pid, appname, oom_score_adj); - - if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED) - continue; - - if (i != 0) - make_memps_log(MEMPS_LOG_FILE, pid, appname); - } -} - static char *convert_to_str(unsigned int mem_state) { char *tmp = NULL; switch (mem_state) { case MEMNOTIFY_NORMAL: - case MEMNOTIFY_RECLAIM: tmp = "mem normal"; break; case MEMNOTIFY_LOW: @@ -812,49 +412,6 @@ static void print_lowmem_state(unsigned int mem_state) convert_to_str(mem_state)); } -static void lowmem_swap_memory(void) -{ - pid_t pid; - int swap_type; - - if (cur_mem_state == MEMNOTIFY_NORMAL) - return; - - swap_type = swap_status(SWAP_GET_TYPE, NULL); - - if (swap_type == SWAP_ON) { - while (1) - { - pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL); - if (!pid) - break; - _I("swap cgroup entered : pid : %d", (int)pid); - resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid); - } - if (swap_status(SWAP_GET_STATUS, NULL) == SWAP_OFF) - resourced_notify(RESOURCED_NOTIFIER_SWAP_RESTART, NULL); - resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL); - } -} - -static int memory_reclaim_act(void *data) -{ - int ret, status; - _I("[LOW MEM STATE] memory reclaim state"); - ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status); - if (ret != 0) { - _E("vconf get failed(VCONFKEY_SYSMAN_LOW_MEMORY)\n"); - return RESOURCED_ERROR_FAIL; - } - if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) - vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, - VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL); - else - lowmem_swap_memory(); - - return 0; -} - static int memory_low_act(void *data) { _I("[LOW MEM STATE] memory low state"); @@ -863,6 +420,7 @@ static int memory_low_act(void *data) vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING); + memory_level_send_system_event(MEMORY_LEVEL_LOW); return 0; } @@ -885,19 +443,7 @@ static int memory_oom_act(void *ad) vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING); - return 1; -} - -static int memory_cgroup_oom_act(int memcg_index) -{ - _I("[LOW MEM STATE] memory oom state"); - - print_mem_state(); - - lowmem_cgroup_oom_killer(memcg_index); - - vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, - VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING); + memory_level_send_system_event(MEMORY_LEVEL_CRITICAL); return 1; } @@ -906,6 +452,7 @@ static int memory_normal_act(void *data) _I("[LOW MEM STATE] memory normal state"); vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL); + memory_level_send_system_event(MEMORY_LEVEL_NORMAL); return 0; } @@ -931,302 +478,29 @@ static int lowmem_process(unsigned int mem_state, void *ad) return 0; } -static unsigned int lowmem_eventfd_read(int fd) -{ - unsigned int ret; - uint64_t dummy_state; - ret = read(fd, &dummy_state, sizeof(dummy_state)); - return ret; -} - -static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler) -{ - int fd, i, currentoom; - struct ss_main_data *ad = (struct ss_main_data *)data; - - if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) { - _E("ecore_main_fd_handler_active_get error , return\n"); - return ECORE_CALLBACK_CANCEL; - } - - fd = ecore_main_fd_handler_fd_get(fd_handler); - if (fd < 0) { - _E("ecore_main_fd_handler_fd_get error , return\n"); - return ECORE_CALLBACK_CANCEL; - } - lowmem_eventfd_read(fd); - - for (i = 0; i < MEMCG_GROUP_MAX; i++) { - currentoom = get_current_oom(i); - if (currentoom == MEMGC_OOM_NORMAL) { - if (memcg_class[i].oomalert) - memory_normal_act(ad); - } - if (currentoom > memcg_class[i].oomlevel) { - switch (currentoom) { - case MEMGC_OOM_WARNING: - memory_low_act(ad); - break; - case MEMGC_OOM_HIGH: - memcg_class[i].oomalert = 1; - memory_cgroup_oom_act(i); - break; - case MEMGC_OOM_CRITICAL: - memcg_class[i].oomalert = 1; - break; - default: - break; - } - } - memcg_class[i].oomlevel = currentoom; - } - - return ECORE_CALLBACK_RENEW; -} - -/* -From memory.txt kernel document - -To register a notifier, application need: -- create an eventfd using eventfd(2) -- open memory.oom_control file -- write string like "<event_fd> <fd of memory.oom_control>" -to cgroup.event_control -*/ - -static int setup_eventfd(void) +static unsigned int lowmem_read(int fd) { - unsigned int thres, i; - int mcgfd, cgfd, evfd, res, sz, ret = -1; - char buf[LOWMEM_PATH_MAX] = {0,}; - - /* create an eventfd using eventfd(2) - use same event fd for using ecore event loop */ - evfd = eventfd(0, 0); - ret = fcntl(evfd, F_SETFL, O_NONBLOCK); - if (ret < 0) + unsigned int mem_state; + if (read(fd, &mem_state, sizeof(mem_state)) < 0) { + _E("error lowmem state"); return RESOURCED_ERROR_FAIL; - - for (i = 0; i < MEMCG_GROUP_MAX; i++) { - /* open cgroup.event_control */ - sprintf(buf, "%s/%s/cgroup.event_control", - MEMCG_PATH, memcg_class[i].cgroup_name); - cgfd = open(buf, O_WRONLY); - if (cgfd < 0) { - _E("open event_control failed"); - return RESOURCED_ERROR_FAIL; - } - - /* register event in usage_in_byte */ - sprintf(buf, "%s/%s/memory.usage_in_bytes", - MEMCG_PATH, memcg_class[i].cgroup_name); - mcgfd = open(buf, O_RDONLY); - if (mcgfd < 0) { - _E("open memory control failed"); - close(cgfd); - return RESOURCED_ERROR_FAIL; - } - - /* threshold lv 1 : wakeup softswapd */ - /* write event fd about threshold lv1 */ - thres = memcg_class[i].thres_lv1; - sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres); - sz += 1; - res = write(cgfd, buf, sz); - if (res != sz) { - _E("write cgfd failed : %d", res); - close(cgfd); - close(mcgfd); - return RESOURCED_ERROR_FAIL; - } - - /* calculate threshold lv_2 */ - /* threshold lv 2 : lowmem warning */ - thres = memcg_class[i].thres_lv2; - - /* write event fd about threshold lv1 */ - sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres); - sz += 1; - res = write(cgfd, buf, sz); - if (res != sz) { - _E("write cgfd failed : %d", res); - close(cgfd); - close(mcgfd); - return RESOURCED_ERROR_FAIL; - } - - /* calculate threshold lv_3 */ - /* threshold lv 3 : victim kill */ - thres = memcg_class[i].thres_lv3; - - /* write event fd about threshold lv2 */ - sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres); - sz += 1; - res = write(cgfd, buf, sz); - if (res != sz) { - _E("write cgfd failed : %d", res); - close(cgfd); - close(mcgfd); - return RESOURCED_ERROR_FAIL; - } - close(mcgfd); - - /* register event in oom_control */ - sprintf(buf, "%s/%s/memory.oom_control", - MEMCG_PATH, memcg_class[i].cgroup_name); - - mcgfd = open(buf, O_RDONLY); - if (mcgfd < 0) { - _E("open memory control failed"); - close(cgfd); - return RESOURCED_ERROR_FAIL; - } - - /* write event fd about oom control with zero threshold*/ - thres = 0; - sz = sprintf(buf, "%d %d %d", evfd, mcgfd, thres); - sz += 1; - res = write(cgfd, buf, sz); - if (res != sz) { - _E("write cgfd failed : %d", res); - close(cgfd); - close(mcgfd); - return RESOURCED_ERROR_FAIL; - } - close(cgfd); - close(mcgfd); } - return evfd; -} - -void set_threshold(int level, int thres) -{ - return; -} - -void set_leave_threshold(int thres) -{ - return; -} - -static int init_memcg(void) -{ - unsigned int total, i, limit, size; - char buf[LOWMEM_PATH_MAX] = {0,}; - FILE *f; - total = _get_total_memory(); - _D("Total : %d", total); - - for (i = 0; i < MEMCG_GROUP_MAX; i++) { - /* write limit_in_bytes */ - sprintf(buf, "%s/%s/memory.limit_in_bytes", - MEMCG_PATH, memcg_class[i].cgroup_name); - _D("buf : %s", buf); - f = fopen(buf, "w"); - if (!f) { - _E("%s open failed", buf); - return RESOURCED_ERROR_FAIL; - } - - limit = (unsigned int)(memcg_class[i].limit_ratio*(float)total); - - if (limit > memcg_class[i].min_limit) - limit = memcg_class[i].min_limit; - - size = sprintf(buf, "%u", limit); - if (fwrite(buf, size, 1, f) != 1) - _E("fwrite memory.limit_in_bytes : %d\n", limit); - fclose(f); - - /* save memory limitation for calculating threshold */ - memcg_class[i].total_limit = limit; - - _calc_threshold(i, limit); - - /* set leave threshold value to kernel */ - lowmem_set_cgroup_leave_threshold(memcg_class[i].oomleave); - - /* enable cgroup move */ - sprintf(buf, "%s/%s/memory.move_charge_at_immigrate", - MEMCG_PATH, memcg_class[i].cgroup_name); - _D("buf : %s", buf); - f = fopen(buf, "w"); - if (!f) { - _E("%s open failed", buf); - return RESOURCED_ERROR_FAIL; - } - size = sprintf(buf, "3"); - if (fwrite(buf, size, 1, f) != 1) - _E("fwrite memory.move_charge_at_immigrate\n"); - fclose(f); - - } - return 0; + return mem_state; } -static void lowmem_move_memcgroup(int pid, int oom_score_adj) +int lowmem_memory_oom_killer(int flags) { - char buf[LOWMEM_PATH_MAX] = {0,}; - FILE *f; - int size, background = 0; - unsigned long swap_args[1] = {0,}; - - if (oom_score_adj > OOMADJ_BACKGRD_LOCKED) { - sprintf(buf, "%s/background/cgroup.procs", MEMCG_PATH); - background = 1; - } - else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED && - oom_score_adj < OOMADJ_BACKGRD_LOCKED) - sprintf(buf, "%s/foreground/cgroup.procs", MEMCG_PATH); - else - return; - - swap_args[0] = (unsigned long)pid; - if (!swap_status(SWAP_CHECK_PID, swap_args) || !background) { - _I("buf : %s, pid : %d, oom : %d", buf, pid, oom_score_adj); - f = fopen(buf, "w"); - if (!f) { - _E("%s open failed", buf); - return; - } - size = sprintf(buf, "%d", pid); - if (fwrite(buf, size, 1, f) != 1) - _E("fwrite cgroup tasks : %d\n", pid); - fclose(f); - } - if (background) - lowmem_swap_memory(); + return memory_oom_act(NULL); } -static void lowmem_cgroup_foregrd_manage(int currentpid) +void lowmem_memcg_set_threshold(int idx, int level, int value) { - char buf[LOWMEM_PATH_MAX] = {0,}; - int pid, pgid; - FILE *f; - sprintf(buf, "%s/background/cgroup.procs", MEMCG_PATH); - f = fopen(buf, "r"); - if (!f) { - _E("%s open failed", buf); - return; - } - while (fgets(buf, LOWMEM_PATH_MAX, f) != NULL) { - pid = atoi(buf); - if (currentpid == pid) - continue; - pgid = getpgid(pid); - if (currentpid == pgid) - lowmem_move_memcgroup(pid, OOMADJ_APP_LIMIT); - } - fclose(f); + /* This function is for vmpressure */ } -static unsigned int lowmem_read(int fd) +void lowmem_memcg_set_leave_threshold(int idx, int value) { - unsigned int mem_state; - if (read(fd, &mem_state, sizeof(mem_state)) < 0) { - _E("error lowmem state"); - return RESOURCED_ERROR_FAIL; - } - return mem_state; + /* This function is for vmpressure */ } static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler) @@ -1262,7 +536,7 @@ static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler) static int lowmem_fd_start(void) { - lowmem_fd = open("/dev/lowmemnotify", O_RDONLY); + lowmem_fd = open(DEV_MEMNOTIFY, O_RDONLY); if (lowmem_fd < 0) { _E("lowmem_fd_start fd open failed"); return RESOURCED_ERROR_FAIL; @@ -1284,13 +558,6 @@ int lowmem_init(void) { int ret = RESOURCED_ERROR_NONE; - /* set default memcg value */ - ret = init_memcg(); - if (ret < 0) { - _E("memory cgroup init failed"); - return RESOURCED_ERROR_FAIL; - } - ret = lowmem_fd_start(); if (ret < 0) { _E("lowmem_fd_start fail\n"); @@ -1304,18 +571,6 @@ int lowmem_init(void) return RESOURCED_ERROR_FAIL; } - /* register threshold and event fd */ - lowmem_fd = setup_eventfd(); - if (lowmem_fd < 0) { - _E("setup event fd is failed"); - return RESOURCED_ERROR_FAIL; - } - - ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ, - (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL); - - _I("lowmem_swaptype : %d", swap_status(SWAP_GET_TYPE, NULL)); - lowmem_dbus_init(); return 0; @@ -1336,22 +591,7 @@ static int lowmem_fd_stop(int fd) static int resourced_memory_control(void *data) { - int ret = RESOURCED_ERROR_NONE; - struct lowmem_data_type *l_data; - - l_data = (struct lowmem_data_type *)data; - switch(l_data->control_type) { - case LOWMEM_MOVE_CGROUP: - if (l_data->args) - lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]); - break; - case LOWMEM_MANAGE_FOREGROUND: - if (l_data->args) - lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]); - break; - - } - return ret; + return RESOURCED_ERROR_NONE; } static int resourced_memory_init(void *data) @@ -1368,14 +608,6 @@ static int resourced_memory_finalize(void *data) int lowmem_control(enum lowmem_control_type type, unsigned long *args) { - struct lowmem_data_type l_data; - - if (lowmem_ops) { - l_data.control_type = type; - l_data.args = args; - return lowmem_ops->control(&l_data); - } - return RESOURCED_ERROR_NONE; } diff --git a/src/memory/lowmem-handler.h b/src/memory/lowmem-handler.h index d938ddde..4981f5c8 100644 --- a/src/memory/lowmem-handler.h +++ b/src/memory/lowmem-handler.h @@ -27,28 +27,27 @@ #define __LOWMEM_HANDLER_H__ void lowmem_dbus_init(void); -int lowmem_oom_killer_cb(int memcg_idx, int flags); +int lowmem_memory_oom_killer(int flags); void lowmem_dynamic_process_killer(int type); unsigned int get_available(void); void change_memory_state(int state, int force); +void lowmem_memcg_set_threshold(int idx, int level, int value); +void lowmem_memcg_set_leave_threshold(int idx, int value); -void set_threshold(int level, int thres); -void set_leave_threshold(int thres); - -#define NUM_FOREGROUND 3 enum { MEMCG_MEMORY, MEMCG_FOREGROUND, - MEMCG_BACKGROUND = MEMCG_FOREGROUND + NUM_FOREGROUND, - MEMCG_MAX_GROUPS, + MEMCG_BACKGROUND, + MEMCG_SWAP, + MEMCG_MAX, }; enum { - MEMNOTIFY_NORMAL, - MEMNOTIFY_SWAP, - MEMNOTIFY_LOW, - MEMNOTIFY_MEDIUM, - MEMNOTIFY_MAX_LEVELS, + LOWMEM_NORMAL, + LOWMEM_SWAP, + LOWMEM_LOW, + LOWMEM_MEDIUM, + LOWMEM_MAX_LEVEL, }; enum oom_killer_cb_flags { diff --git a/src/memory/memcontrol.c b/src/memory/memcontrol.c new file mode 100644 index 00000000..adec82c7 --- /dev/null +++ b/src/memory/memcontrol.c @@ -0,0 +1,146 @@ +/* + * resourced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @file memcontrol.c + * + * @desc structure and operation for memory cgroups + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include <string.h> +#include <stdio.h> + +#include "resourced.h" +#include "trace.h" +#include "macro.h" +#include "memcontrol.h" + +void memcg_info_set_limit(struct memcg_info_t *mi, float ratio, + unsigned int totalram) +{ + if (!mi) + return; + + mi->limit = (float)totalram * ratio; + mi->limit_ratio = ratio; + mi->threshold[LOWMEM_LOW] = (unsigned int)(mi->limit * MEMCG_LOW_RATIO); + mi->threshold[LOWMEM_MEDIUM] = (unsigned int)(mi->limit * MEMCG_MEDIUM_RATIO); + mi->threshold_leave = (float)mi->limit * MEMCG_FOREGROUND_LEAVE_RATIO; + mi->oomleave = mi->limit - mi->threshold_leave; +} + +static struct memcg_info_t *memcg_info_alloc_subcgroup(struct memcg_t *memcg) +{ + struct memcg_info_t *pmi, *mi; + int i; + if (!memcg) + return NULL; + mi = (struct memcg_info_t *)malloc(sizeof(struct memcg_info_t)); + if (!mi) + return NULL; + mi->id = memcg->num_subcgroup; + pmi = memcg->info; + snprintf(mi->name, MAX_PATH_LENGTH, "%s%d/", + pmi->name, memcg->num_subcgroup++); + mi->limit_ratio = pmi->limit_ratio; + mi->limit = pmi->limit; + mi->oomleave = pmi->oomleave; + for (i = 0; i < LOWMEM_MAX_LEVEL; i++) + mi->threshold[i] = pmi->threshold[i]; + mi->threshold_leave = pmi->threshold_leave; + strncpy(mi->event_level, pmi->event_level, + MAX_NAME_LENGTH); + mi->evfd = pmi->evfd; + return mi; +} + +static int memcg_add_cgroup(struct memcg_t *memcg) +{ + struct memcg_info_t *mi = NULL; + if (!memcg) + return RESOURCED_ERROR_FAIL; + mi = memcg_info_alloc_subcgroup(memcg); + if (!mi) + return RESOURCED_ERROR_FAIL; + memcg->cgroups = g_slist_prepend(memcg->cgroups, mi); + return RESOURCED_ERROR_NONE; +} + +int memcg_add_cgroups(struct memcg_t *memcg, int num) +{ + int i, ret = RESOURCED_ERROR_NONE; + for (i = 0; i < num; i++) { + ret = memcg_add_cgroup(memcg); + if (ret == RESOURCED_ERROR_FAIL) + return ret; + } + return ret; +} + +static void memcg_info_show(struct memcg_info_t *mi) +{ + int i; + _D("======================================"); + _D("memcg_info->name = %s", mi->name); + _D("memcg_info->limit_ratio = %.f", mi->limit_ratio); + _D("memcg_info->limit = %u", mi->limit); + _D("memcg_info->oomleave = %u", mi->oomleave); + for (i = 0; i < LOWMEM_MAX_LEVEL; i++) + _D("memcg_info->threshold = %u", mi->threshold[i]); + _D("memcg_info->threshold_leave = %u", mi->threshold_leave); + _D("memcg_info->event_level = %s", mi->event_level); + _D("memcg_info->evfd = %d", mi->evfd); +} + +void memcg_show(struct memcg_t *memcg) +{ + GSList *iter = NULL; + memcg_info_show(memcg->info); + gslist_for_each_item(iter, memcg->cgroups) { + struct memcg_info_t *mi = + (struct memcg_info_t *)(iter->data); + memcg_info_show(mi); + } +} + +void memcg_info_init(struct memcg_info_t *mi, const char *name) +{ + int i; + mi->id = 0; + mi->limit_ratio = 0; + mi->limit = 0; + mi->oomleave = 0; + for (i = 0; i < LOWMEM_MAX_LEVEL; i++) + mi->threshold[i] = 0; + mi->threshold_leave = 0; + mi->evfd = -1; + strncpy(mi->event_level, MEMCG_DEFAULT_EVENT_LEVEL, + MAX_NAME_LENGTH); + strncpy(mi->name, name, MAX_PATH_LENGTH); +} + +void memcg_init(struct memcg_t *memcg) +{ + memcg->num_subcgroup = MEMCG_DEFAULT_NUM_SUBCGROUP; + memcg->use_hierarchy = MEMCG_DEFAULT_USE_HIERARCHY; + memcg->info = NULL; + memcg->cgroups = NULL; +} diff --git a/src/memory/memcontrol.h b/src/memory/memcontrol.h new file mode 100644 index 00000000..6bab45f2 --- /dev/null +++ b/src/memory/memcontrol.h @@ -0,0 +1,77 @@ +/* + * resourced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * @file memcontrol.h + * @desc header file for handling memory cgroups + **/ + +#ifndef __MEMCONTROL_H__ +#define __MEMCONTROL_H__ + +#include <glib.h> +#include "lowmem-handler.h" +#include "const.h" + +/* number of memory cgroups */ +#define MEMCG_DEFAULT_NUM_SUBCGROUP 0 +#define MEMCG_DEFAULT_EVENT_LEVEL "medium" +#define MEMCG_DEFAULT_USE_HIERARCHY 0 + +#define MEMCG_LOW_RATIO 0.8 +#define MEMCG_MEDIUM_RATIO 0.96 +#define MEMCG_FOREGROUND_LEAVE_RATIO 0.25 + +struct memcg_info_t { + /* name of memory cgroup */ + char name[MAX_PATH_LENGTH]; + /* id for sub cgroup. 0 if no hierarchy, 0 ~ MAX if use hierarchy */ + int id; + /* limit ratio, if don't want to set limit, use NO_LIMIT*/ + float limit_ratio; + unsigned int limit; + /* leave memory usage */ + unsigned int oomleave; + /* thresholds, normal, swap, low, medium, and leave */ + unsigned int threshold[LOWMEM_MAX_LEVEL]; + unsigned int threshold_leave; + /* vmpressure event string. If don't want to register event, use null */ + char event_level[MAX_NAME_LENGTH]; + int evfd; +}; + +struct memcg_t { + /* number of sub cgroups */ + int num_subcgroup; + /* parent cgroup */ + struct memcg_info_t *info; + /* set when using multiple sub cgroups */ + int use_hierarchy; + /* list of child cgroups when using multi groups */ + GSList *cgroups; +}; + +void memcg_info_set_limit(struct memcg_info_t *memcg_info, float ratio, + unsigned int totalram); +void memcg_info_init(struct memcg_info_t *memcg_info, const char *name); +void memcg_init(struct memcg_t *memcg); +void memcg_show(struct memcg_t *memcg); +int memcg_add_cgroups(struct memcg_t *memcg, int num); + +#endif /*__MEMCONTROL_H__*/ diff --git a/src/memory/memory_eng.conf b/src/memory/memory_eng.conf index 81751d2f..c6a47582 100644 --- a/src/memory/memory_eng.conf +++ b/src/memory/memory_eng.conf @@ -53,14 +53,51 @@ ThresholdSwap=100 # MB ThresholdLow=60 # MB # Threshold to start low memory killer -ThresholdMedium=40 # MB +ThresholdMedium=50 # MB # Threshold to stop low memory killer ThresholdLeave=70 # MB +# Threshold for dynamic memory killer +DynamicThreshold=80 # MB + +# Leave Threshold for dynamic memory killer +DynamicLeave=100 # MB + +# Foreground limit ratio +ForegroundRatio=0.6 + +# Number of max victims +NumMaxVictims=5 + +[Memory768] +# Threshold to start swap +ThresholdSwap=300 # MB + +# Threshold to start reclaim +ThresholdLow=200 # MB + +# Threshold to start low memory killer +ThresholdMedium=100 # MB + +# Threshold to stop low memory killer +ThresholdLeave=150 # MB + +# Threshold for dynamic memory killer +DynamicThreshold=150 # MB + +# Leave Threshold for dynamic memory killer +DynamicLeave=230 # MB + # Foreground limit ratio ForegroundRatio=0.6 +# Foreground use hierarchy +# ForegroundUseHierarchy=1 + +# Foreground # of cgroups +# ForegroundNumCgroups=3 + # Number of max victims NumMaxVictims=5 diff --git a/src/memory/memory_user.conf b/src/memory/memory_user.conf index f821ba23..8d5e0389 100644 --- a/src/memory/memory_user.conf +++ b/src/memory/memory_user.conf @@ -50,17 +50,48 @@ ThresholdSwap=100 # MB ThresholdLow=60 # MB # Threshold to start low memory killer -ThresholdMedium=40 # MB +ThresholdMedium=50 # MB # Threshold to stop low memory killer ThresholdLeave=70 # MB +# Threshold for dynamic memory killer +DynamicThreshold=80 # MB + +# Leave Threshold for dynamic memory killer +DynamicLeave=100 # MB + # Foreground limit ratio ForegroundRatio=1 # Number of max victims NumMaxVictims=5 +[Memory768] +# Threshold to start swap +ThresholdSwap=300 # MB + +# Threshold to start reclaim +ThresholdLow=200 # MB + +# Threshold to start low memory killer +ThresholdMedium=100 # MB + +# Threshold to stop low memory killer +ThresholdLeave=150 # MB + +# Threshold for dynamic memory killer +DynamicThreshold=150 # MB + +# Leave Threshold for dynamic memory killer +DynamicLeave=230 # MB + +# Foreground limit ratio +ForegroundRatio=0.6 + +# Number of max victims +NumMaxVictims=5 + [Memory1024] # Threshold to start swap ThresholdSwap=300 # MB diff --git a/src/memory/vmpressure-lowmem-handler.c b/src/memory/vmpressure-lowmem-handler.c index 0c0db5cb..27da2171 100644 --- a/src/memory/vmpressure-lowmem-handler.c +++ b/src/memory/vmpressure-lowmem-handler.c @@ -1,7 +1,7 @@ /* * resourced * - * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2012 - 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,11 +17,11 @@ */ /* - * @file lowmem_handler.c + * @file vmpressure-lowmem-handler.c * * @desc lowmem handler using memcgroup * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. * */ @@ -57,9 +57,15 @@ #include "config-parser.h" #include "module.h" #include "logging-common.h" +#include "swap-common.h" +#include "cgroup.h" +#include "memcontrol.h" +#include "helper.h" + +#define LOWMEM_DEFAULT_CGROUP "/sys/fs/cgroup/memory" +#define LOWMEM_NO_LIMIT 0 +#define LOWMEM_THRES_INIT 0 -#define MEMINFO_PATH "/proc/meminfo" -#define MEMCG_PATH "/sys/fs/cgroup" #define MEMPS_LOG_PATH "/var/log/" #define MEMPS_LOG_FILE MEMPS_LOG_PATH"memps" #define MEMPS_EXEC_PATH "usr/bin/memps" @@ -74,7 +80,6 @@ #define BtoKB(x) ((x) >> 10) #define BtoPAGE(x) ((x) >> 12) -#define NO_LIMIT -1 /* for memory cgroup, set no limit */ #define MEMCG_MEMORY_LIMIT_RATIO NO_LIMIT #define MEMCG_FOREGROUND_LIMIT_RATIO 1 @@ -82,13 +87,9 @@ #define MEMCG_BACKGROUND_LIMIT_RATIO NO_LIMIT #define MEMCG_FOREGROUND_MIN_LIMIT UINT_MAX #define MEMCG_BACKGROUND_MIN_LIMIT UINT_MAX -#define MEMCG_LOW_RATIO 0.8 -#define MEMCG_MEDIUM_RATIO 0.96 #define MEMCG_FOREGROUND_THRES_LEAVE 100 /* MB */ -#define MEMCG_FOREGROUND_LEAVE_RATIO 0.25 #define BUF_MAX 1024 -#define LOWMEM_PATH_MAX 100 #define MAX_MEMORY_CGROUP_VICTIMS 10 #define MAX_CGROUP_VICTIMS 1 #define OOM_TIMER_INTERVAL 2 @@ -105,6 +106,7 @@ #define MEM_SIZE_64 64 /* MB */ #define MEM_SIZE_256 256 /* MB */ #define MEM_SIZE_512 512 /* MB */ +#define MEM_SIZE_768 768 /* MB */ #define MEM_SIZE_1024 1024 /* MB */ #define MEM_SIZE_2048 2048 /* MB */ @@ -127,13 +129,23 @@ /* threshold for 512M RAM */ #define DYNAMIC_PROCESS_512_THRES 80 /* MB */ #define DYNAMIC_PROCESS_512_LEAVE 100 /* MB */ -#define DYNAMIC_PROCESS_512_THRESLAUNCH 60 /* MB */ -#define DYNAMIC_PROCESS_512_LEAVELAUNCH 80 /* MB */ +#define DYNAMIC_PROCESS_512_THRESLAUNCH 60 /* MB */ +#define DYNAMIC_PROCESS_512_LEAVELAUNCH 80 /* MB */ #define MEMCG_MEMORY_512_THRES_SWAP 100 /* MB */ #define MEMCG_MEMORY_512_THRES_LOW 50 /* MB */ #define MEMCG_MEMORY_512_THRES_MEDIUM 40 /* MB */ #define MEMCG_MEMORY_512_THRES_LEAVE 60 /* MB */ +/* threshold for 768 RAM */ +#define DYNAMIC_PROCESS_768_THRES 100 /* MB */ +#define DYNAMIC_PROCESS_768_LEAVE 120 /* MB */ +#define DYNAMIC_PROCESS_768_THRESLAUNCH 60 /* MB */ +#define DYNAMIC_PROCESS_768_LEAVELAUNCH 80 /* MB */ +#define MEMCG_MEMORY_768_THRES_SWAP 150 /* MB */ +#define MEMCG_MEMORY_768_THRES_LOW 100 /* MB */ +#define MEMCG_MEMORY_768_THRES_MEDIUM 60 /* MB */ +#define MEMCG_MEMORY_768_THRES_LEAVE 100 /* MB */ + /* threshold for more than 1024M RAM */ #define DYNAMIC_PROCESS_1024_THRES 150 /* MB */ #define DYNAMIC_PROCESS_1024_LEAVE 300 /* MB */ @@ -150,7 +162,7 @@ #define MEMCG_MEMORY_2048_THRES_MEDIUM 160 /* MB */ #define MEMCG_MEMORY_2048_THRES_LEAVE 300 /* MB */ -static int thresholds[MEMNOTIFY_MAX_LEVELS]; + static int dynamic_process_threshold[DYNAMIC_KILL_MAX]; static int dynamic_process_leave[DYNAMIC_KILL_MAX]; @@ -161,38 +173,19 @@ struct task_info { int size; }; -struct memcg_class { - unsigned int min_limit; /* minimum limit */ - /* limit ratio, if don't want to set limit, use NO_LIMIT*/ - float limit_ratio; - unsigned int oomleave; /* leave memory usage */ - char *cgroup_name; /* cgroup name */ - unsigned int thres_low; /* low level threshold */ - unsigned int thres_medium; /* medium level threshold */ - unsigned int thres_leave; /* leave threshold */ - /* vmpressure event string. If don't want to register event, use null */ - char *event_string; - /* compare function for selecting victims in each cgroup */ - int (*compare_fn) (const struct task_info *, const struct task_info *); -}; - struct lowmem_process_entry { int cur_mem_state; int new_mem_state; void (*action) (void); }; - struct victims { int num; pid_t pids[MAX_MEMORY_CGROUP_VICTIMS]; }; /* low memory action function for cgroup */ -static void memory_cgroup_medium_act(int memcg_idx); -static int compare_mem_victims(const struct task_info *ta, const struct task_info *tb); -static int compare_bg_victims(const struct task_info *ta, const struct task_info *tb); -static int compare_fg_victims(const struct task_info *ta, const struct task_info *tb); +static void memory_cgroup_medium_act(int idx, struct memcg_info_t *mi); /* low memory action function */ static void normal_act(void); static void swap_act(void); @@ -202,7 +195,7 @@ static void medium_act(void); static Eina_Bool medium_cb(void *data); #define LOWMEM_ENTRY(c, n, act) \ - { MEMNOTIFY_##c, MEMNOTIFY_##n, act} + { LOWMEM_##c, LOWMEM_##n, act} static struct lowmem_process_entry lpe[] = { LOWMEM_ENTRY(NORMAL, SWAP, swap_act), @@ -219,36 +212,7 @@ static struct lowmem_process_entry lpe[] = { LOWMEM_ENTRY(MEDIUM, LOW, low_act), }; -static struct memcg_class memcg_class[MEMCG_MAX_GROUPS] = { - {NO_LIMIT, MEMCG_MEMORY_LIMIT_RATIO, - 0, "memory", - 0, 0, - 0, "medium", /* register medium event*/ - compare_mem_victims}, - {MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, - 0, "memory/foreground1", - 0, 0, - MEMCG_FOREGROUND_THRES_LEAVE, "medium", - compare_fg_victims}, - {MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, - 0, "memory/foreground2", - 0, 0, - MEMCG_FOREGROUND_THRES_LEAVE, "medium", - compare_fg_victims}, - {MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, - 0, "memory/foreground3", - 0, 0, - MEMCG_FOREGROUND_THRES_LEAVE, "medium", - compare_fg_victims}, - {MEMCG_BACKGROUND_MIN_LIMIT, MEMCG_BACKGROUND_LIMIT_RATIO, - 0, "memory/background", - 0, 0, - 0, NULL, /* register no event*/ - compare_bg_victims}, -}; - -static int evfd[MEMCG_MAX_GROUPS] = {-1, }; -static int cur_mem_state = MEMNOTIFY_NORMAL; +static int cur_mem_state = LOWMEM_NORMAL; static Ecore_Timer *oom_check_timer = NULL; static Ecore_Timer *oom_sigkill_timer = NULL; static pid_t killed_fg_victim; @@ -264,6 +228,17 @@ static unsigned long ktotalram; static const struct module_ops memory_modules_ops; static const struct module_ops *lowmem_ops; +static char *memcg_name[MEMCG_MAX] = { + NULL, + "foreground", + "background", + "swap", +}; + +struct memcg_t **memcg; +struct memcg_info_t *memcg_memory; + +static int compare_victims(const struct task_info *ta, const struct task_info *tb); static inline void get_total_memory(void) { @@ -277,50 +252,6 @@ static inline void get_total_memory(void) } } -unsigned int get_available(void) -{ - char buf[PATH_MAX]; - FILE *fp; - char *idx; - unsigned int free = 0, cached = 0; - unsigned int available = 0; - - fp = fopen(MEMINFO_PATH, "r"); - - if (!fp) { - _E("%s open failed, %d", buf, fp); - return available; - } - - while (fgets(buf, PATH_MAX, fp) != NULL) { - if ((idx = strstr(buf, "MemFree:"))) { - idx += strlen("MemFree:"); - while (*idx < '0' || *idx > '9') - idx++; - free = atoi(idx); - } else if ((idx = strstr(buf, "MemAvailable:"))) { - idx += strlen("MemAvailable:"); - while (*idx < '0' || *idx > '9') - idx++; - available = atoi(idx); - break; - } else if((idx = strstr(buf, "Cached:"))) { - idx += strlen("Cached:"); - while (*idx < '0' || *idx > '9') - idx++; - cached = atoi(idx); - break; - } - } - - if (available == 0) - available = free + cached; - available >>= 10; - fclose(fp); - - return available; -} - static bool get_mem_usage_by_pid(pid_t pid, unsigned int *rss) { FILE *fp; @@ -343,14 +274,14 @@ static bool get_mem_usage_by_pid(pid_t pid, unsigned int *rss) return true; } -static unsigned int get_mem_usage(int idx) +unsigned int get_cgroup_mem_usage(const char *cgroup_name) { FILE *f; - char buf[LOWMEM_PATH_MAX] = {0,}; + char buf[MAX_PATH_LENGTH] = {0,}; unsigned int usage; - sprintf(buf, "%s/%s/memory.usage_in_bytes", - MEMCG_PATH, memcg_class[idx].cgroup_name); + sprintf(buf, "%smemory.usage_in_bytes", cgroup_name); + _D("get mem usage from %s", buf); f = fopen(buf, "r"); if (!f) { @@ -368,16 +299,16 @@ static unsigned int get_mem_usage(int idx) return usage; } -static int get_mem_usage_anon(int idx, unsigned int *result) +static int get_mem_usage_anon(struct memcg_info_t *mi, unsigned int *result) { FILE *f; - char buf[LOWMEM_PATH_MAX] = {0,}; + char buf[BUF_MAX] = {0,}; char line[BUF_MAX] = {0, }; - char name[30] = {0, }; + char name[BUF_MAX] = {0, }; unsigned int tmp, active_anon = 0, inactive_anon = 0; - sprintf(buf, "%s/%s/memory.stat", - MEMCG_PATH, memcg_class[idx].cgroup_name); + sprintf(buf, "%smemory.stat", mi->name); + _I("get mem usage anon from %s", buf); f = fopen(buf, "r"); if (!f) { @@ -475,13 +406,13 @@ static void make_memps_log(char *file, pid_t pid, char *victim_name) } } -static int lowmem_check_current_state(int memcg_index) +static int lowmem_check_current_state(struct memcg_info_t *mi) { unsigned int usage, oomleave; int ret; - oomleave = memcg_class[memcg_index].oomleave; - ret = get_mem_usage_anon(memcg_index, &usage); + oomleave = mi->oomleave; + ret = get_mem_usage_anon(mi, &usage); if (ret) { _D("getting anonymous usage fails"); @@ -499,77 +430,21 @@ static int lowmem_check_current_state(int memcg_index) } } -static int compare_mem_victims(const struct task_info *ta, const struct task_info *tb) -{ - int pa, pb; - assert(ta != NULL); - assert(tb != NULL); - - /* - * Weight task size ratio to totalram by OOM_SCORE_POINT_WEIGHT so that - * tasks with score -1000 or -900 could be selected as victims if they consumes - * memory more than 70% of totalram. - */ - pa = (int)(ta->size * OOM_SCORE_POINT_WEIGHT) / ktotalram + ta->oom_score_adj; - pb = (int)(tb->size * OOM_SCORE_POINT_WEIGHT) / ktotalram + tb->oom_score_adj; - - return (pb - pa); -} - -static int compare_bg_victims(const struct task_info *ta, const struct task_info *tb) -{ - /* - * Firstly, sort by oom_score_adj - * Secondly, sort by task size - */ - assert(ta != NULL); - assert(tb != NULL); - - if (ta->oom_score_adj != tb->oom_score_adj) - return (tb->oom_score_adj - ta->oom_score_adj); - - return ((int)(tb->size) - (int)(ta->size)); -} - -static int compare_fg_victims(const struct task_info *ta, const struct task_info *tb) -{ - /* - * only sort by task size - */ - assert(ta != NULL); - assert(tb != NULL); - - return ((int)(tb->size) - (int)(ta->size)); -} - -static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info *selected, - unsigned should_be_freed, int flags) +static int lowmem_get_pids_memcg(int idx, struct memcg_info_t *mi, GArray *pids) { + char buf[BUF_MAX] = {0,}; + char appname[BUF_MAX] = {0,}; FILE *f = NULL; - char buf[LOWMEM_PATH_MAX] = {0, }; - int i = 0; - int num_victims = 0; - unsigned total_victim_size = 0; - char appname[PATH_MAX] = {0, }; - - GArray *victim_candidates = NULL; - - victim_candidates = g_array_new(false, false, sizeof(struct task_info)); - - /* if g_array_new fails, return the current number of victims */ - if (victim_candidates == NULL) - return num_victims; + int i; if (idx == MEMCG_MEMORY) { - sprintf(buf, "%s/%s/system.slice/cgroup.procs", - MEMCG_PATH, memcg_class[idx].cgroup_name); + sprintf(buf, "%ssystem.slice/cgroup.procs", mi->name); f = fopen(buf, "r"); } if (!f) { - sprintf(buf, "%s/%s/cgroup.procs", - MEMCG_PATH, memcg_class[idx].cgroup_name); + sprintf(buf, "%scgroup.procs", mi->name); f = fopen(buf, "r"); if (!f) { _E("%s open failed, %d", buf, f); @@ -577,7 +452,7 @@ static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info * if task read in this cgroup fails, * return the current number of victims */ - return num_victims; + return pids->len; } } @@ -602,8 +477,8 @@ static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info if(proc_get_cmdline(tpid, appname) == RESOURCED_ERROR_FAIL) continue; - for (i = 0; i < victim_candidates->len; i++) { - struct task_info *tsk = &g_array_index(victim_candidates, + for (i = 0; i < pids->len; i++) { + struct task_info *tsk = &g_array_index(pids, struct task_info, i); if (getpgid(tpid) == tsk->pgid) { tsk->size += tsize; @@ -615,28 +490,50 @@ static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info } } - if (i == victim_candidates->len) { + if (i == pids->len) { new_victim.pid = tpid; new_victim.pgid = getpgid(tpid); new_victim.oom_score_adj = toom; new_victim.size = tsize; - g_array_append_val(victim_candidates, new_victim); + g_array_append_val(pids, new_victim); } } + fclose(f); + return pids->len; + +} + + +static int lowmem_get_cgroup_victims(int idx, struct memcg_info_t *mi, + int max_victims, struct task_info *selected, + unsigned should_be_freed, int flags) +{ + int i = 0; + int num_victims = 0; + unsigned total_victim_size = 0; + GArray *victim_candidates = NULL; + int count = 0; + + victim_candidates = g_array_new(false, false, sizeof(struct task_info)); + + /* if g_array_new fails, return the current number of victims */ + if (victim_candidates == NULL) + return num_victims; + /* * if there is no tasks in this cgroup, * return the current number of victims */ - if (victim_candidates->len == 0) { + count = lowmem_get_pids_memcg(idx, mi, victim_candidates); + if (count == 0) { g_array_free(victim_candidates, true); - fclose(f); return num_victims; } g_array_sort(victim_candidates, - (GCompareFunc)memcg_class[idx].compare_fn); + (GCompareFunc)compare_victims); for (i = 0; i < victim_candidates->len; i++) { struct task_info tsk; @@ -648,16 +545,19 @@ static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info tsk = g_array_index(victim_candidates, struct task_info, i); - if (tsk.oom_score_adj < OOMADJ_BACKGRD_UNLOCKED) { + if ((idx >= MEMCG_BACKGROUND) && + (tsk.oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)) { unsigned int available; if ((flags & OOM_FORCE) || !(flags & OOM_TIMER_CHECK)) { - _D("%d is skipped during force kill", tsk.pid); + _D("%d is skipped during force kill, flag = %d", + tsk.pid, flags); break; } available = get_available(); if ((flags & OOM_TIMER_CHECK) && - (available > thresholds[MEMNOTIFY_MEDIUM] + - THRESHOLD_MARGIN)) { + (available > + memcg_memory->threshold[LOWMEM_MEDIUM] + + THRESHOLD_MARGIN)) { _D("available: %d MB, larger than threshold margin", available); break; @@ -669,12 +569,12 @@ static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info selected[num_victims].oom_score_adj = tsk.oom_score_adj; selected[num_victims].size = tsk.size; total_victim_size += tsk.size >> 10; + _D("selected[%d].pid = %d", num_victims, tsk.pid); num_victims++; } g_array_free(victim_candidates, true); - fclose(f); return num_victims; } @@ -688,17 +588,17 @@ static int lowmem_swap_cgroup_oom_killer(int flags) int ret; char appname[PATH_MAX]; int count = 0; - char buf[LOWMEM_PATH_MAX] = {0, }; + char buf[BUF_MAX] = {0, }; FILE *f; unsigned int tsize = 0; - int swap_type; + const char *name; - swap_type = swap_status(SWAP_GET_TYPE, NULL); - if (swap_type <= SWAP_OFF) - return count; + if (!memcg[MEMCG_SWAP] || !memcg[MEMCG_SWAP]->info) + return RESOURCED_ERROR_FAIL; - sprintf(buf, "%s/memory/swap/cgroup.procs", - MEMCG_PATH); + name = memcg[MEMCG_SWAP]->info->name; + + sprintf(buf, "%scgroup.procs", name); f = fopen(buf, "r"); if (!f) { @@ -722,7 +622,6 @@ static int lowmem_swap_cgroup_oom_killer(int flags) continue; } - /* To Do: skip by checking pgid? */ if (toom < OOMADJ_BACKGRD_UNLOCKED) continue; @@ -730,15 +629,15 @@ static int lowmem_swap_cgroup_oom_killer(int flags) if (ret == RESOURCED_ERROR_FAIL) continue; - /* make memps log for killing application firstly */ if (count == 0) make_memps_log(MEMPS_LOG_FILE, tpid, appname); count++; - proc_remove_process_list(tpid); kill(tpid, SIGKILL); - _E("we killed, lowmem lv2 = %d (%s) score = %d, size = %u KB\n", + + proc_remove_process_list(tpid); + _E("we killed, %d (%s) score = %d, size = %u KB\n", tpid, appname, toom, tsize); if (is_dynamic_process_killer(flags) && count >= MAX_SWAP_VICTIMS) @@ -754,68 +653,137 @@ static int lowmem_swap_cgroup_oom_killer(int flags) return count; } +static int lowmem_get_subcgroup_victims(int idx, struct task_info *selected, + int max_victims, int flags) +{ + GSList *iter = NULL; + GArray *pids = NULL; + int num_victims, count; + unsigned int total_victim_size = 0; + unsigned int available; + + pids = g_array_new(false, false, sizeof(struct task_info)); + gslist_for_each_item(iter, memcg[idx]->cgroups) { + struct memcg_info_t *mi = + (struct memcg_info_t *)(iter->data); + count = lowmem_get_pids_memcg(idx, mi, pids); + _D("get %d pids", count); + } + + g_array_sort(pids, (GCompareFunc)compare_victims); + + for (num_victims = 0; num_victims < pids->len; num_victims++) { + struct task_info tsk; + if (num_victims == max_victims) + break; + + tsk = g_array_index(pids, struct task_info, num_victims); + + if ((idx >= MEMCG_BACKGROUND) && + (tsk.oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)) { + if ((flags & OOM_FORCE) || !(flags & OOM_TIMER_CHECK)) { + _D("%d is skipped during force kill, flag = %d", + tsk.pid, flags); + break; + } + available = get_available(); + if ((flags & OOM_TIMER_CHECK) && + (available > + memcg_memory->threshold[LOWMEM_MEDIUM] + + THRESHOLD_MARGIN)) { + _D("available: %d MB, larger than threshold margin", + available); + break; + } + } + + selected[num_victims].pid = tsk.pid; + selected[num_victims].pgid = tsk.pgid; + selected[num_victims].oom_score_adj = tsk.oom_score_adj; + selected[num_victims].size = tsk.size; + total_victim_size += tsk.size >> 10; + _D("selected[%d].pid = %d, total size = %u", num_victims, tsk.pid, total_victim_size); + } + + g_array_free(pids, true); + return num_victims; +} + /* Find victims: (SWAP -> ) BACKGROUND */ -static int lowmem_get_memory_cgroup_victims(struct task_info *selected, - int flags) +static int lowmem_get_memory_cgroup_victims(struct task_info *selected, int flags) { int i, swap_victims, count = 0; - unsigned int available, should_be_freed = 0; + unsigned int available, should_be_freed = 0, leave_threshold; + struct memcg_info_t *mi; + unsigned int threshold; + /* To Do: swap cgroup will be treated like other cgroups */ swap_victims = lowmem_swap_cgroup_oom_killer(flags); if ((flags & OOM_FORCE) && swap_victims < MAX_FD_VICTIMS) { count = lowmem_get_cgroup_victims(MEMCG_BACKGROUND, - MAX_FD_VICTIMS - swap_victims, selected, - 0, flags); - _D("kill %d victims in %s cgroup", - count, memcg_class[MEMCG_BACKGROUND].cgroup_name); + memcg[MEMCG_BACKGROUND]->info, + MAX_FD_VICTIMS - swap_victims, + selected, 0, flags); + _D("kill %d victims in background cgroup", count); return count; } available = get_available(); - if (available < memcg_class[MEMCG_MEMORY].thres_leave) - should_be_freed = memcg_class[MEMCG_MEMORY].thres_leave - available; + leave_threshold = memcg_memory->threshold_leave; + if (available < leave_threshold) + should_be_freed = leave_threshold - available; _I("should_be_freed = %u MB", should_be_freed); if (should_be_freed == 0 || swap_victims >= num_max_victims) return count; - for (i = MEMCG_MAX_GROUPS - 1; i >= 0; i--) { - if ((flags & OOM_TIMER_CHECK) && i < MEMCG_BACKGROUND && - available > thresholds[MEMNOTIFY_MEDIUM]) { + for (i = MEMCG_MAX - 1; i >= 0; i--) { + if (!memcg[i] || !memcg[i]->info) + continue; + mi = memcg[i]->info; + threshold = mi->threshold[LOWMEM_MEDIUM]; + + if ((flags & OOM_TIMER_CHECK) && (i < MEMCG_BACKGROUND) && + (available > threshold)) { _D("in timer, not kill fg app, available %u > threshold %u", - available, thresholds[MEMNOTIFY_MEDIUM]); + available, threshold); return count; } - count = lowmem_get_cgroup_victims(i, - num_max_victims - swap_victims, - selected, should_be_freed, flags); + if (!memcg[i]->use_hierarchy) + count = lowmem_get_cgroup_victims(i, mi, + num_max_victims - swap_victims, + selected, should_be_freed, flags); + else + /* To Do: include max num victims in memcg_t and use it */ + count = lowmem_get_subcgroup_victims(i, selected, + num_max_victims, flags); if (count > 0) { - _D("kill %d victims in %s cgroup", - count, memcg_class[i].cgroup_name); + _D("get %d victims in %s cgroup", count, + mi->name); return count; } else _D("There are no victims to be killed in %s cgroup", - memcg_class[i].cgroup_name); + mi->name); } return count; } -static int lowmem_get_victims(int idx, struct task_info *selected, - int flags) +static int lowmem_get_victims(int idx, struct memcg_info_t *mi, + struct task_info *selected, int flags) { int count = 0; if (idx == MEMCG_MEMORY) count = lowmem_get_memory_cgroup_victims(selected, flags); else - count = lowmem_get_cgroup_victims(idx, - MAX_CGROUP_VICTIMS, selected, - memcg_class[idx].thres_leave, - flags); + count = lowmem_get_cgroup_victims(idx, mi, + MAX_CGROUP_VICTIMS, selected, + mi->threshold_leave, + flags); return count; } @@ -845,13 +813,10 @@ static int lowmem_kill_victims(int memcg_idx, int pid, ret, oom_score_adj, i; unsigned total_size = 0, size; char appname[PATH_MAX]; + int sigterm = 0; + int app_flag = 0; for (i = 0; i < count; i++) { - /* check current memory status */ - if (!(flags & OOM_FORCE) && memcg_idx != MEMCG_MEMORY && - lowmem_check_current_state(memcg_idx) >= 0) - return count; - pid = selected[i].pid; oom_score_adj = selected[i].oom_score_adj; size = selected[i].size; @@ -877,16 +842,24 @@ static int lowmem_kill_victims(int memcg_idx, total_size += size; - proc_remove_process_list(pid); - if (flags & OOM_FORCE) { + if (oom_score_adj < OOMADJ_BACKGRD_LOCKED) { + sigterm = 1; + } else if (oom_score_adj == OOMADJ_BACKGRD_LOCKED) { + app_flag = proc_get_apptype(pid); + sigterm = app_flag & PROC_SIGTERM; + } + + if (sigterm) { kill(pid, SIGTERM); if (killed_tasks.num < MAX_MEMORY_CGROUP_VICTIMS) killed_tasks.pids[killed_tasks.num++] = pid; } else kill(pid, SIGKILL); - _E("we killed, force(%d), lowmem lv2 = %d (%s) score = %d, size = %u KB, victim total size = %u KB\n", - flags & OOM_FORCE, pid, appname, oom_score_adj, size, total_size); + proc_remove_process_list(pid); + _E("we killed, force(%d), %d (%s) score = %d, size = %u KB, victim total size = %u KB, sigterm = %d\n", + flags & OOM_FORCE, pid, appname, oom_score_adj, + size, total_size, sigterm); if (memcg_idx >= MEMCG_FOREGROUND && memcg_idx < MEMCG_BACKGROUND) @@ -902,27 +875,45 @@ static int lowmem_kill_victims(int memcg_idx, return count; } -int lowmem_oom_killer_cb(int memcg_idx, int flags) +static int lowmem_oom_killer_cb(int idx, struct memcg_info_t *mi, int flags) { struct task_info selected[MAX_MEMORY_CGROUP_VICTIMS] = {{0, 0, OOMADJ_SU, 0}, }; int count = 0; /* get multiple victims from /sys/fs/cgroup/memory/.../tasks */ - count = lowmem_get_victims(memcg_idx, selected, flags); + count = lowmem_get_victims(idx, mi, selected, flags); if (count == 0) { - _D("get %s cgroup victim is failed", - memcg_class[memcg_idx].cgroup_name); + _D("victim count = %d", count); return count; } - count = lowmem_kill_victims(memcg_idx, count, selected, flags); + /* check current memory status */ + if (!(flags & OOM_FORCE) && idx != MEMCG_MEMORY && + lowmem_check_current_state(mi) >= 0) + return count; + + + count = lowmem_kill_victims(idx, count, selected, flags); clear_logs(MEMPS_LOG_PATH); logging_control(LOGGING_UPDATE_STATE, NULL); + if (killed_tasks.num > 0 && oom_sigkill_timer == NULL) { + _D("start timer to sigkill tasks"); + oom_sigkill_timer = + ecore_timer_add(OOM_SIGKILL_WAIT, send_sigkill_cb, + NULL); + } else + killed_tasks.num = 0; + return count; } +int lowmem_memory_oom_killer(int flags) +{ + return lowmem_oom_killer_cb(MEMCG_MEMORY, memcg_memory, flags); +} + void lowmem_dynamic_process_killer(int type) { struct task_info selected[MAX_MEMORY_CGROUP_VICTIMS] = {{0, 0, OOMADJ_SU, 0}, }; @@ -931,6 +922,7 @@ void lowmem_dynamic_process_killer(int type) unsigned should_be_freed; int flags = OOM_FORCE; int swap_victims; + struct memcg_info_t *mi = memcg[MEMCG_BACKGROUND]->info; if (!dynamic_process_threshold[type]) return; @@ -938,7 +930,7 @@ void lowmem_dynamic_process_killer(int type) if (available >= dynamic_process_threshold[type]) return; - change_memory_state(MEMNOTIFY_LOW, 1); + change_memory_state(LOWMEM_LOW, 1); swap_victims = lowmem_swap_cgroup_oom_killer(flags); if (swap_victims >= MAX_SWAP_VICTIMS) goto start_timer; @@ -949,7 +941,7 @@ void lowmem_dynamic_process_killer(int type) should_be_freed = dynamic_process_leave[type] - available; _D("run dynamic killer, type=%d, available=%d, should_be_freed = %u", type, available, should_be_freed); - count = lowmem_get_cgroup_victims(MEMCG_BACKGROUND, + count = lowmem_get_cgroup_victims(MEMCG_BACKGROUND, mi, num_max_victims - swap_victims, selected, should_be_freed, flags); @@ -959,7 +951,7 @@ void lowmem_dynamic_process_killer(int type) } lowmem_kill_victims(MEMCG_BACKGROUND, count, selected, flags); - change_memory_state(MEMNOTIFY_NORMAL, 0); + change_memory_state(LOWMEM_NORMAL, 0); start_timer: if (oom_sigkill_timer == NULL) { @@ -996,7 +988,7 @@ static void *lowmem_oom_killer_pthread(void *arg) } _I("oom thread conditional signal received and start"); - lowmem_oom_killer_cb(MEMCG_MEMORY, OOM_NONE); + lowmem_oom_killer_cb(MEMCG_MEMORY, memcg_memory, OOM_NONE); _I("lowmem_oom_killer_cb finished"); ret = pthread_mutex_unlock(&oom_mutex); @@ -1016,16 +1008,16 @@ static char *convert_to_str(int mem_state) { char *tmp = NULL; switch (mem_state) { - case MEMNOTIFY_NORMAL: + case LOWMEM_NORMAL: tmp = "mem normal"; break; - case MEMNOTIFY_SWAP: + case LOWMEM_SWAP: tmp = "mem swap"; break; - case MEMNOTIFY_LOW: + case LOWMEM_LOW: tmp = "mem low"; break; - case MEMNOTIFY_MEDIUM: + case LOWMEM_MEDIUM: tmp = "mem medium"; break; default: @@ -1046,27 +1038,10 @@ static void change_lowmem_state(unsigned int mem_state) static void lowmem_swap_memory(void) { - pid_t pid; - int swap_type; - - if (cur_mem_state == MEMNOTIFY_NORMAL) + if (cur_mem_state == LOWMEM_NORMAL) return; - swap_type = swap_status(SWAP_GET_TYPE, NULL); - - if (swap_type == SWAP_ON) { - while (1) - { - pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL); - if (!pid) - break; - _I("swap cgroup entered : pid : %d", (int)pid); - resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid); - } - if (swap_status(SWAP_GET_STATUS, NULL) == SWAP_OFF) - resourced_notify(RESOURCED_NOTIFIER_SWAP_RESTART, NULL); - resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL); - } + resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL); } @@ -1077,11 +1052,13 @@ static void normal_act(void) ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status); if (ret) _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY); - if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) + if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) { vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL); + memory_level_send_system_event(MEMORY_LEVEL_NORMAL); + } - change_lowmem_state(MEMNOTIFY_NORMAL); + change_lowmem_state(LOWMEM_NORMAL); } static void swap_act(void) @@ -1092,10 +1069,12 @@ static void swap_act(void) if (ret) _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY); - if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) + if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL) { vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL); - change_lowmem_state(MEMNOTIFY_SWAP); + memory_level_send_system_event(MEMORY_LEVEL_NORMAL); + } + change_lowmem_state(LOWMEM_SWAP); } static void low_act(void) @@ -1107,25 +1086,27 @@ static void low_act(void) if (ret) _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY); - change_lowmem_state(MEMNOTIFY_LOW); + change_lowmem_state(LOWMEM_LOW); /* Since vconf for soft warning could be set during low memory check, * we set it only when the current status is not soft warning. */ - if (status != VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) + if (status != VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING) { vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING); + memory_level_send_system_event(MEMORY_LEVEL_LOW); + } } static Eina_Bool medium_cb(void *data) { - unsigned int available; + unsigned int available, threshold; int count = 0; available = get_available(); _D("available = %u, timer run until reaching leave threshold", available); - if (available >= memcg_class[MEMCG_MEMORY].thres_leave && oom_check_timer != NULL) { + if (available >= memcg_memory->threshold_leave && oom_check_timer != NULL) { ecore_timer_del(oom_check_timer); oom_check_timer = NULL; _D("oom_check_timer deleted after reaching leave threshold"); @@ -1134,18 +1115,20 @@ static Eina_Bool medium_cb(void *data) } _I("cannot reach leave threshold, timer again"); - count = lowmem_oom_killer_cb(MEMCG_MEMORY, OOM_TIMER_CHECK); + count = lowmem_oom_killer_cb(MEMCG_MEMORY, + memcg_memory, OOM_TIMER_CHECK); /* * After running oom killer in timer, but there is no victim, * stop timer. */ - if (!count && available >= thresholds[MEMNOTIFY_MEDIUM] && + threshold = memcg_memory->threshold[LOWMEM_MEDIUM]; + if (!count && available >= threshold && oom_check_timer != NULL) { ecore_timer_del(oom_check_timer); oom_check_timer = NULL; _D("oom_check_timer deleted, available %u > threshold %u", - available, thresholds[MEMNOTIFY_MEDIUM]); + available, threshold); normal_act(); return ECORE_CALLBACK_CANCEL; } @@ -1156,7 +1139,7 @@ static void medium_act(void) { int ret = 0; - change_lowmem_state(MEMNOTIFY_MEDIUM); + change_lowmem_state(LOWMEM_MEDIUM); /* signal to lowmem_oom_killer_pthread to start killer */ ret = pthread_mutex_trylock(&oom_mutex); @@ -1171,6 +1154,7 @@ static void medium_act(void) vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY, VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING); + memory_level_send_system_event(MEMORY_LEVEL_CRITICAL); if (oom_check_timer == NULL) { _D("timer run until reaching leave threshold"); @@ -1198,54 +1182,14 @@ static int lowmem_process(int mem_state) return RESOURCED_ERROR_NONE; } -static bool is_fg_victim_killed(int memcg_idx) -{ - if (killed_fg_victim) { - char buf[LOWMEM_PATH_MAX] = {0, }; - FILE *f; - sprintf(buf, "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH, - memcg_idx); - f = fopen(buf, "r"); - if (!f) { - _E("%s open failed, %d", buf, f); - /* if file open fails, start to kill */ - return true; - } - - while (fgets(buf, 32, f) != NULL) { - pid_t pid = atoi(buf); - - /* - * not yet removed from foreground cgroup, - * so, not start to kill again - */ - if (killed_fg_victim == pid) { - fclose(f); - return false; - } - } - - /* - * in this case, memory is low even though the previous - * fg victim was already killed. so, start to kill. - */ - fclose(f); - killed_fg_victim = 0; - return true; - } - - return true; -} - -static void show_foreground_procs(int memcg_idx) { - char buf[LOWMEM_PATH_MAX] = {0, }; +static void show_foreground_procs(struct memcg_info_t *mi) { + char buf[BUF_MAX] = {0, }; FILE *f; - sprintf(buf, "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH, - memcg_idx); + sprintf(buf, "%scgroup.procs", mi->name); + f = fopen(buf, "r"); if (!f) { _E("%s open failed, %d", buf, f); - /* if file open fails, start to kill */ return; } @@ -1259,17 +1203,14 @@ static void show_foreground_procs(int memcg_idx) { fclose(f); } -static void memory_cgroup_medium_act(int memcg_idx) +static void memory_cgroup_medium_act(int idx, struct memcg_info_t *mi) { _I("[LOW MEM STATE] memory cgroup %s oom state", - memcg_class[memcg_idx].cgroup_name); + mi->name); - /* only start to kill fg victim when no pending fg victim */ - if ((memcg_idx >= MEMCG_FOREGROUND && memcg_idx < MEMCG_BACKGROUND) - && is_fg_victim_killed(memcg_idx)) { - show_foreground_procs(memcg_idx); - lowmem_oom_killer_cb(memcg_idx, OOM_NONE); - } + /* To Do: only start to kill fg victim when no pending fg victim */ + show_foreground_procs(mi); + lowmem_oom_killer_cb(idx, mi, OOM_NONE); } static unsigned int lowmem_eventfd_read(int fd) @@ -1283,8 +1224,8 @@ static unsigned int lowmem_eventfd_read(int fd) static unsigned int check_mem_state(unsigned int available) { int mem_state; - for (mem_state = MEMNOTIFY_MAX_LEVELS -1; mem_state > MEMNOTIFY_NORMAL; mem_state--) { - if (available <= thresholds[mem_state]) + for (mem_state = LOWMEM_MAX_LEVEL -1; mem_state > LOWMEM_NORMAL; mem_state--) { + if (available <= memcg_memory->threshold[mem_state]) break; } @@ -1305,16 +1246,16 @@ void change_memory_state(int state, int force) } switch (mem_state) { - case MEMNOTIFY_NORMAL: + case LOWMEM_NORMAL: normal_act(); break; - case MEMNOTIFY_SWAP: + case LOWMEM_SWAP: swap_act(); break; - case MEMNOTIFY_LOW: + case LOWMEM_LOW: low_act(); break; - case MEMNOTIFY_MEDIUM: + case LOWMEM_MEDIUM: medium_act(); break; default: @@ -1322,7 +1263,7 @@ void change_memory_state(int state, int force) } } -static void lowmem_handler(void) +static void lowmem_memory_handler(void) { static unsigned int prev_available; unsigned int available; @@ -1338,28 +1279,30 @@ static void lowmem_handler(void) prev_available = available; } -static void lowmem_cgroup_handler(int memcg_idx) +static void lowmem_cgroup_handler(int idx, struct memcg_info_t *mi) { - unsigned int usage; + unsigned int usage, threshold; int ret; - ret = get_mem_usage_anon(memcg_idx, &usage); + ret = get_mem_usage_anon(mi, &usage); if (ret) { _D("getting anonymous memory usage fails"); return; } - - if (usage >= memcg_class[memcg_idx].thres_medium) - memory_cgroup_medium_act(memcg_idx); + threshold = mi->threshold[LOWMEM_MEDIUM]; + if (usage >= threshold) + memory_cgroup_medium_act(idx, mi); else _I("anon page (%u) is under medium threshold (%u)", - usage >> 20, memcg_class[memcg_idx].thres_medium >> 20); + usage >> 20, threshold >> 20); } static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler) { int fd, i; + struct memcg_info_t *mi; + GSList *iter = NULL; if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) { _E("ecore_main_fd_handler_active_get error , return\n"); @@ -1373,12 +1316,26 @@ static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler) } lowmem_eventfd_read(fd); - for (i = 0; i < MEMCG_MAX_GROUPS; i++) { - if (fd == evfd[i]) { - if (i == MEMCG_MEMORY) { - lowmem_handler(); - } else { - lowmem_cgroup_handler(i); + for (i = 0; i < MEMCG_MAX; i++) { + if (!memcg[i] || !memcg[i]->info) + continue; + mi = memcg[i]->info; + if (fd == mi->evfd) { + /* call low memory handler for this memcg */ + if (i == MEMCG_MEMORY) + lowmem_memory_handler(); + else + lowmem_cgroup_handler(i, mi); + return ECORE_CALLBACK_RENEW; + } + /* ToDo: iterate child memcgs */ + gslist_for_each_item(iter, memcg[i]->cgroups) { + mi = (struct memcg_info_t *)(iter->data); + if (fd == mi->evfd) { + lowmem_cgroup_handler(i, mi); + _D("lowmem cgroup handler is called for %s", + mi->name); + return ECORE_CALLBACK_RENEW; } } } @@ -1395,115 +1352,84 @@ To register a notifier, application need: to cgroup.event_control */ -static int setup_eventfd(void) +static void register_eventfd(struct memcg_info_t *mi) { - unsigned int i; - int cgfd, pressurefd, res, sz; - char buf[LOWMEM_PATH_MAX] = {0,}; - - - for (i = 0; i < MEMCG_MAX_GROUPS; i++) { - if (memcg_class[i].event_string == NULL) - continue; - /* open cgroup.event_control */ - sprintf(buf, "%s/%s/cgroup.event_control", - MEMCG_PATH, memcg_class[i].cgroup_name); - cgfd = open(buf, O_WRONLY); - if (cgfd < 0) { - _E("open event_control failed"); - return RESOURCED_ERROR_FAIL; - } - - /* register event pressure_level */ - sprintf(buf, "%s/%s/memory.pressure_level", - MEMCG_PATH, memcg_class[i].cgroup_name); - pressurefd = open(buf, O_RDONLY); - if (pressurefd < 0) { - _E("open pressure control failed"); - close(cgfd); - return RESOURCED_ERROR_FAIL; - } + int cgfd, pressurefd, evfd, res, sz; + char buf[BUF_MAX] = {0,}; + const char *name = mi->name; - /* create an eventfd using eventfd(2) - use same event fd for using ecore event loop */ - evfd[i] = eventfd(0, O_NONBLOCK); - if (evfd[i] < 0) { - _E("eventfd() error"); - close(cgfd); - close(pressurefd); - return RESOURCED_ERROR_FAIL; - } - - /* pressure level*/ - /* write event fd low level */ - sz = sprintf(buf, "%d %d %s", evfd[i], pressurefd, - memcg_class[i].event_string); - sz += 1; - res = write(cgfd, buf, sz); - if (res != sz) { - _E("write cgfd failed : %d for %s", - res, memcg_class[i].cgroup_name); - close(cgfd); - close(pressurefd); - close(evfd[i]); - evfd[i] = -1; - return RESOURCED_ERROR_FAIL; - } + if (mi->threshold[LOWMEM_MEDIUM] == LOWMEM_THRES_INIT) + return; - _I("register event fd success for %s cgroup", - memcg_class[i].cgroup_name); - ecore_main_fd_handler_add(evfd[i], ECORE_FD_READ, - (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL); + /* open cgroup.event_control */ + sprintf(buf, "%scgroup.event_control", name); + cgfd = open(buf, O_WRONLY); + if (cgfd < 0) { + _E("open event_control failed"); + return; + } + /* register event pressure_level */ + sprintf(buf, "%smemory.pressure_level", name); + pressurefd = open(buf, O_RDONLY); + if (pressurefd < 0) { + _E("open pressure control failed"); close(cgfd); - close(pressurefd); + return; } - return 0; -} - -static int write_cgroup_node(const char *memcg_name, - const char *file_name, unsigned int value) -{ - FILE *f = NULL; - char buf[LOWMEM_PATH_MAX] = {0, }; - int size; - sprintf(buf, "%s/%s/%s", MEMCG_PATH, memcg_name, file_name); - f = fopen(buf, "w"); - if (!f) { - _E("%s open failed", buf); - return RESOURCED_ERROR_FAIL; + /* create an eventfd using eventfd(2) + use same event fd for using ecore event loop */ + evfd = eventfd(0, O_NONBLOCK); + if (evfd < 0) { + _E("eventfd() error"); + close(cgfd); + close(pressurefd); + return; } + mi->evfd = evfd; - size = sprintf(buf, "%u", value); - if (fwrite(buf, size, 1, f) != 1) { - _E("fail fwrite %s\n", file_name); - fclose(f); - return RESOURCED_ERROR_FAIL; + /* pressure level*/ + /* write event fd low level */ + sz = sprintf(buf, "%d %d %s", evfd, pressurefd, mi->event_level); + sz += 1; + res = write(cgfd, buf, sz); + if (res != sz) { + _E("write cgfd failed : %d for %s", res, name); + close(cgfd); + close(pressurefd); + close(evfd); + mi->evfd = -1; + return; } - fclose(f); - return RESOURCED_ERROR_NONE; -} + _I("register event fd success for %s cgroup", name); + ecore_main_fd_handler_add(evfd, ECORE_FD_READ, + (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL); -void set_threshold(int level, int thres) -{ - thresholds[level] = thres; + close(cgfd); + close(pressurefd); return; } -void set_leave_threshold(int thres) +static int setup_eventfd(void) { - memcg_class[MEMCG_MEMORY].thres_leave = thres; - return; -} + unsigned int i; + struct memcg_info_t *mi; + GSList *iter = NULL; -void set_foreground_ratio(float ratio) -{ - int i; - for (i = MEMCG_FOREGROUND; i < MEMCG_BACKGROUND; i++) - memcg_class[i].limit_ratio = ratio; - return; + for (i = 0; i < MEMCG_MAX; i++) { + if (!memcg[i]->use_hierarchy) { + register_eventfd(memcg[i]->info); + } else { + GSList *list = memcg[i]->cgroups; + gslist_for_each_item(iter, list) { + mi = (struct memcg_info_t *)(iter->data); + register_eventfd(mi); + } + } + } + return RESOURCED_ERROR_NONE; } static int load_mem_config(struct parse_result *result, void *user_data) @@ -1523,6 +1449,17 @@ static int load_mem_config(struct parse_result *result, void *user_data) return RESOURCED_ERROR_NONE; } +void lowmem_memcg_set_threshold(int idx, int level, int value) +{ + memcg[idx]->info->threshold[level] = value; +} + +void lowmem_memcg_set_leave_threshold(int idx, int value) +{ + memcg[idx]->info->threshold_leave = value; +} + + static int set_memory_config(const char * section_name, const struct parse_result *result) { if (!result || !section_name) @@ -1533,19 +1470,27 @@ static int set_memory_config(const char * section_name, const struct parse_resul if (!strcmp(result->name, "ThresholdSwap")) { int value = atoi(result->value); - set_threshold(MEMNOTIFY_SWAP, value); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, value); } else if (!strcmp(result->name, "ThresholdLow")) { int value = atoi(result->value); - set_threshold(MEMNOTIFY_LOW, value); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, value); } else if (!strcmp(result->name, "ThresholdMedium")) { int value = atoi(result->value); - set_threshold(MEMNOTIFY_MEDIUM, value); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, value); } else if (!strcmp(result->name, "ThresholdLeave")) { int value = atoi(result->value); - set_leave_threshold(value); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, value); } else if (!strcmp(result->name, "ForegroundRatio")) { - float value = atof(result->value); - set_foreground_ratio(value); + float ratio = atof(result->value); + memcg_info_set_limit(memcg[MEMCG_FOREGROUND]->info, ratio, totalram); + } else if (!strcmp(result->name, "ForegroundUseHierarchy")) { + int use_hierarchy = atoi(result->value); + memcg[MEMCG_FOREGROUND]->use_hierarchy = use_hierarchy; + } else if (!strcmp(result->name, "ForegroundNumCgroups")) { + int num_cgroups = atoi(result->value); + memcg_add_cgroups(memcg[MEMCG_FOREGROUND], + num_cgroups); + memcg_show(memcg[MEMCG_FOREGROUND]); } else if (!strcmp(result->name, "NumMaxVictims")) { int value = atoi(result->value); num_max_victims = value; @@ -1589,6 +1534,11 @@ static int memory_load_512_config(struct parse_result *result, void *user_data) return set_memory_config("Memory512", result); } +static int memory_load_768_config(struct parse_result *result, void *user_data) +{ + return set_memory_config("Memory768", result); +} + static int memory_load_1024_config(struct parse_result *result, void *user_data) { return set_memory_config("Memory1024", result); @@ -1599,30 +1549,29 @@ static int memory_load_2048_config(struct parse_result *result, void *user_data) return set_memory_config("Memory2048", result); } -/* init thresholds depending on total ram size. */ -static void init_thresholds(void) +/* setup memcg parameters depending on total ram size. */ +static void setup_memcg_params(void) { int i; unsigned long total_ramsize = BtoMB(totalram); _D("Total : %lu MB", total_ramsize); - if (total_ramsize <= MEM_SIZE_64) { /* set thresholds for ram size 64M */ dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_64_THRES; dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_64_LEAVE; - set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_64_THRES_SWAP); - set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_64_THRES_LOW); - set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_64_THRES_MEDIUM); - set_leave_threshold(MEMCG_MEMORY_64_THRES_LEAVE); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_64_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_64_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_64_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_64_THRES_LEAVE); config_parse(MEM_CONF_FILE, memory_load_64_config, NULL); } else if (total_ramsize <= MEM_SIZE_256) { /* set thresholds for ram size 256M */ dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_256_THRES; dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_256_LEAVE; - set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_256_THRES_SWAP); - set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_256_THRES_LOW); - set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_256_THRES_MEDIUM); - set_leave_threshold(MEMCG_MEMORY_256_THRES_LEAVE); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_256_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_256_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_256_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_256_THRES_LEAVE); config_parse(MEM_CONF_FILE, memory_load_256_config, NULL); } else if (total_ramsize <= MEM_SIZE_512) { /* set thresholds for ram size 512M */ @@ -1630,101 +1579,160 @@ static void init_thresholds(void) dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_512_LEAVE; dynamic_process_threshold[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_512_THRESLAUNCH; dynamic_process_leave[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_512_LEAVELAUNCH; - set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_512_THRES_SWAP); - set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_512_THRES_LOW); - set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_512_THRES_MEDIUM); - set_leave_threshold(MEMCG_MEMORY_512_THRES_LEAVE); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_512_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_512_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_512_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_512_THRES_LEAVE); config_parse(MEM_CONF_FILE, memory_load_512_config, NULL); + } else if (total_ramsize <= MEM_SIZE_768) { + /* set thresholds for ram size 512M */ + dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_768_THRES; + dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_768_LEAVE; + dynamic_process_threshold[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_768_THRESLAUNCH; + dynamic_process_leave[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_768_LEAVELAUNCH; + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_768_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_768_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_768_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_768_THRES_LEAVE); + config_parse(MEM_CONF_FILE, memory_load_768_config, NULL); } else if (total_ramsize <= MEM_SIZE_1024) { /* set thresholds for ram size more than 1G */ dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_1024_THRES; dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_1024_LEAVE; - set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_1024_THRES_SWAP); - set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_1024_THRES_LOW); - set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_1024_THRES_MEDIUM); - set_leave_threshold(MEMCG_MEMORY_1024_THRES_LEAVE); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_1024_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_1024_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_1024_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_1024_THRES_LEAVE); config_parse(MEM_CONF_FILE, memory_load_1024_config, NULL); } else { dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_2048_THRES; dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_2048_LEAVE; - set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_2048_THRES_SWAP); - set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_2048_THRES_LOW); - set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_2048_THRES_MEDIUM); - set_leave_threshold(MEMCG_MEMORY_2048_THRES_LEAVE); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_2048_THRES_SWAP); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_2048_THRES_LOW); + lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_2048_THRES_MEDIUM); + lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_2048_THRES_LEAVE); config_parse(MEM_CONF_FILE, memory_load_2048_config, NULL); } - for (i = MEMNOTIFY_SWAP; i < MEMNOTIFY_MAX_LEVELS; i++) - _I("set threshold for %d to %u", i, thresholds[i]); + for (i = LOWMEM_SWAP; i < LOWMEM_MAX_LEVEL; i++) + _I("set threshold for %d to %u", i, memcg_memory->threshold[i]); - _I("set thres_leave to %u", memcg_class[MEMCG_MEMORY].thres_leave); + _I("set thres_leave to %u", memcg_memory->threshold_leave); _I("set dynamic process threshold to %u", dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP]); _I("set dynamic process leave to %u", dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP]); } -static int create_foreground_memcg(void) +static int compare_victims(const struct task_info *ta, const struct task_info *tb) { - int i; - char buf[LOWMEM_PATH_MAX] = {0, }; - for (i = MEMCG_FOREGROUND; i < MEMCG_MAX_GROUPS; i++) { - sprintf(buf, "%s/%s", MEMCG_PATH, memcg_class[i].cgroup_name); - if (mkdir(buf, 0755) && errno != EEXIST) { - _E("mkdir %s failed, errno %d", buf, errno); + /* + * Firstly, sort by oom_score_adj + * Secondly, sort by task size + */ + assert(ta != NULL); + assert(tb != NULL); + + if (ta->oom_score_adj != tb->oom_score_adj) + return (tb->oom_score_adj - ta->oom_score_adj); + + return ((int)(tb->size) - (int)(ta->size)); +} + +static int init_memcg_params(void) +{ + int idx = 0; + char buf[MAX_PATH_LENGTH]; + memcg = (struct memcg_t **)malloc(sizeof(struct memcg_t *) * + MEMCG_MAX); + if (!memcg) + return RESOURCED_ERROR_FAIL; + + for (idx = 0; idx < MEMCG_MAX; idx++) { + struct memcg_info_t *mi = NULL; + memcg[idx] = (struct memcg_t *)malloc(sizeof(struct memcg_t)); + if (!memcg[idx]) { + int i; + for (i = 0; i < idx - 1; i++) + free(memcg[i]); + free(memcg); + return RESOURCED_ERROR_FAIL; + } + memcg_init(memcg[idx]); + if (memcg_name[idx]) + snprintf(buf, MAX_PATH_LENGTH, "%s/%s/", LOWMEM_DEFAULT_CGROUP, + memcg_name[idx]); + else + snprintf(buf, MAX_PATH_LENGTH, "%s/", LOWMEM_DEFAULT_CGROUP); + mi = (struct memcg_info_t *)malloc(sizeof(struct memcg_info_t)); + if (!mi) { + int i; + for (i = 0; i < idx; i++) + free(memcg[i]); + free(memcg); return RESOURCED_ERROR_FAIL; } - _I("%s is successfuly created", buf); + memcg_info_init(mi, buf); + memcg[idx]->info = mi; + _I("init memory cgroup for %s", buf); + if (idx == MEMCG_MEMORY) + memcg_memory = memcg[idx]->info; } return RESOURCED_ERROR_NONE; } -static int init_memcg(void) +static int write_params_memcg_info(struct memcg_info_t *mi, + int write_limit) { - unsigned int i, limit; - _D("Total : %lu", totalram); + unsigned int limit = mi->limit; + const char *name = mi->name; int ret = RESOURCED_ERROR_NONE; + _I("write memcg param for %s", name); + /* enable cgroup move */ + ret = cgroup_write_node(name, + MEMCG_MOVE_CHARGE_PATH, 3); + if (ret) + return ret; - for (i = 0; i < MEMCG_MAX_GROUPS; i++) { - /* enable cgroup move */ - ret = write_cgroup_node(memcg_class[i].cgroup_name, - MEMCG_MOVE_CHARGE_PATH, 3); - if (ret) - return ret; + /* + * for memcg with LOWMEM_NO_LIMIT or write_limit is not set, + * do not set limit for cgroup limit. + */ + if (mi->limit_ratio == LOWMEM_NO_LIMIT || + !write_limit) + return ret; - /* for memcg with NO_LIMIT, do not set limit for cgroup limit */ - if (memcg_class[i].limit_ratio == NO_LIMIT) - continue; + /* disable memcg OOM-killer */ + ret = cgroup_write_node(name, + MEMCG_OOM_CONTROL_PATH, 1); + if (ret) + return ret; - /* disable memcg OOM-killer */ - ret = write_cgroup_node(memcg_class[i].cgroup_name, - MEMCG_OOM_CONTROL_PATH, 1); - if (ret) - return ret; - - /* write limit_in_bytes */ - limit = (unsigned int)(memcg_class[i].limit_ratio*(float)totalram); - if (limit > memcg_class[i].min_limit) - limit = memcg_class[i].min_limit; - ret = write_cgroup_node(memcg_class[i].cgroup_name, - MEMCG_LIMIT_PATH, limit); - if (ret) - return ret; - else - _I("set %s's limit to %u", memcg_class[i].cgroup_name, limit); + /* write limit_in_bytes */ + ret = cgroup_write_node(name, + MEMCG_LIMIT_PATH, limit); + _I("set %s's limit to %u", name, limit); + return ret; +} - if (BtoMB(totalram) < MEM_SIZE_512 && - (i >= MEMCG_FOREGROUND && i < MEMCG_BACKGROUND)) { - memcg_class[i].thres_leave = limit * MEMCG_FOREGROUND_LEAVE_RATIO; - _I("set foreground%d leave %u for limit %u", - i, memcg_class[i].thres_leave, limit); +static int write_memcg_params(void) +{ + unsigned int i; + _D("Total : %lu", totalram); + int ret = RESOURCED_ERROR_NONE; + GSList *iter = NULL; + + for (i = 0; i < MEMCG_MAX; i++) { + struct memcg_info_t *mi = memcg[i]->info; + int write_limit = !memcg[i]->use_hierarchy; + GSList *list = memcg[i]->cgroups; + write_params_memcg_info(mi, write_limit); + /* write limit to the node for sub cgroups */ + write_limit = 1; + /* write node for sub cgroups */ + gslist_for_each_item(iter, list) { + struct memcg_info_t *mi = + (struct memcg_info_t *)(iter->data); + write_params_memcg_info(mi, write_limit); } - - /* set threshold and oomleave for each memcg */ - memcg_class[i].thres_low = - (unsigned int)(limit * MEMCG_LOW_RATIO); - memcg_class[i].thres_medium = - (unsigned int)(limit * MEMCG_MEDIUM_RATIO); - memcg_class[i].oomleave = - limit - (memcg_class[i].thres_leave << 20); } return ret; @@ -1737,89 +1745,104 @@ static void lowmem_check(void) available = get_available(); _D("available = %u", available); - if(cur_mem_state != MEMNOTIFY_SWAP && - (available <= thresholds[MEMNOTIFY_SWAP] && - available > thresholds[MEMNOTIFY_LOW])) { + if(cur_mem_state != LOWMEM_SWAP && + (available <= memcg_memory->threshold[LOWMEM_SWAP] && + available > memcg_memory->threshold[LOWMEM_LOW])) { swap_act(); } } -static int find_foreground_cgroup(struct proc_process_info_t *process_info) { - int fg, min_fg = -1; +static struct memcg_info_t *find_foreground_cgroup(struct proc_process_info_t *ppi) { + unsigned int usage; unsigned int min_usage = UINT_MAX; + struct memcg_info_t *min_mi = NULL, *mi; + GSList *iter = NULL; /* * if this process group is already in one of the foreground cgroup, * put all of the process in this group into the same cgroup. */ - if (process_info && process_info->memcg_idx >= MEMCG_FOREGROUND && - process_info->memcg_idx < MEMCG_FOREGROUND + NUM_FOREGROUND) - return process_info->memcg_idx; + if (ppi && + ppi->memcg_idx == MEMCG_FOREGROUND) { + _D("%s is already in foreground", ppi->appid); + return ppi->memcg_info; + } /* * if any of the process in this group is not in foreground, * find foreground cgroup with minimum usage */ - for (fg = MEMCG_FOREGROUND; fg < MEMCG_BACKGROUND; fg++) { - unsigned int usage; - usage = get_mem_usage(fg); - - /* select foreground memcg with no task first*/ - if (usage == 0) - return fg; - - /* select forground memcg with minimum usage */ - if (usage > 0 && min_usage > usage) { - min_usage = usage; - min_fg = fg; + if (memcg[MEMCG_FOREGROUND]->use_hierarchy) { + gslist_for_each_item(iter, + memcg[MEMCG_FOREGROUND]->cgroups) { + mi = (struct memcg_info_t *)(iter->data); + const char *name = mi->name; + + usage = get_cgroup_mem_usage(name); + /* select foreground memcg with no task first */ + if (usage == 0) { + _D("%s' usage is 0, selected", name); + return mi; + } + + /* select forground memcg with minimum usage */ + if (usage > 0 && min_usage > usage) { + min_usage = usage; + min_mi = mi; + } } - } + _D("%s is selected at min usage = %u", + min_mi->name, min_usage); - if (min_fg < 0) - return RESOURCED_ERROR_FAIL; + } else { + return memcg[MEMCG_FOREGROUND]->info; + } - return min_fg; + return min_mi; } static void lowmem_move_memcgroup(int pid, int oom_score_adj) { - char buf[LOWMEM_PATH_MAX] = {0,}; + char buf[BUF_MAX] = {0,}; FILE *f; int size, background = 0; - unsigned long swap_args[1] = {0,}; - struct proc_process_info_t *process_info = + const char *name; + struct proc_process_info_t *ppi = find_process_info(NULL, pid, NULL); if (oom_score_adj >= OOMADJ_BACKGRD_LOCKED) { - sprintf(buf, "%s/memory/background/cgroup.procs", MEMCG_PATH); - proc_set_process_info_memcg(process_info, MEMCG_BACKGROUND); + struct memcg_info_t *mi = + memcg[MEMCG_BACKGROUND]->info; + name = mi->name; + sprintf(buf, "%scgroup.procs", name); + proc_set_process_info_memcg(ppi, + MEMCG_BACKGROUND, mi); background = 1; } else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED && - oom_score_adj < OOMADJ_BACKGRD_LOCKED) { - int ret = find_foreground_cgroup(process_info); - if (ret == RESOURCED_ERROR_FAIL) { - _E("cannot find foreground cgroup"); - return; - } - sprintf(buf, "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH, ret); - proc_set_process_info_memcg(process_info, ret); + oom_score_adj < OOMADJ_BACKGRD_LOCKED) { + struct memcg_info_t *mi = + find_foreground_cgroup(ppi); + name = mi->name; + sprintf(buf, "%scgroup.procs", name); + proc_set_process_info_memcg(ppi, + MEMCG_FOREGROUND, mi); } else return; - swap_args[0] = (unsigned long)pid; - if (!swap_status(SWAP_CHECK_PID, swap_args) || !background) { + if (!swap_check_swap_pid(pid) || !background) { _D("buf : %s, pid : %d, score : %d", buf, pid, oom_score_adj); f = fopen(buf, "w"); if (!f) { - _E("%s open failed", buf); - return; + _E("%s open failed", buf); + return; } size = sprintf(buf, "%d", pid); if (fwrite(buf, size, 1, f) != 1) - _E("fwrite cgroup tasks : %d\n", pid); + _E("fwrite cgroup tasks : %d\n", pid); fclose(f); } + if (background) { lowmem_check(); lowmem_swap_memory(); @@ -1829,17 +1852,17 @@ static void lowmem_move_memcgroup(int pid, int oom_score_adj) static void lowmem_cgroup_foregrd_manage(int currentpid) { GSList *iter; - struct proc_process_info_t *process_info = + struct proc_process_info_t *ppi = find_process_info(NULL, currentpid, NULL); - if (!process_info) + if (!ppi) return; - gslist_for_each_item(iter, process_info->pids) { - struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data); + gslist_for_each_item(iter, ppi->pids) { + struct pid_info_t *pi = (struct pid_info_t *)(iter->data); - if (pid_info->type == RESOURCED_APP_TYPE_GROUP) - lowmem_move_memcgroup(pid_info->pid, OOMADJ_FOREGRD_UNLOCKED); + if (pi->type == PROC_TYPE_GROUP) + lowmem_move_memcgroup(pi->pid, OOMADJ_FOREGRD_UNLOCKED); } } @@ -1863,17 +1886,34 @@ static int oom_thread_create(void) return ret; } -static int lowmem_app_launch_cb(void *data) +static int create_memcgs(void) { - struct proc_status *p_data = (struct proc_status*)data; - struct proc_process_info_t *process_info; - int ret = 0; - ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL, - "Please provide valid argument!"); - process_info = (struct proc_process_info_t *)p_data->processinfo; + int i = 0; + int ret = RESOURCED_ERROR_NONE; + GSList *iter = NULL; + struct memcg_info_t *mi; + char *name; + + /* skip for memory cgroup */ + for (i = 0; i < MEMCG_MAX; i++) { + if (!memcg_name[i]) + continue; + mi = memcg[i]->info; + name = mi->name; + ret = make_cgroup_subdir(NULL, name, NULL); + if (!memcg[i]->use_hierarchy) + continue; + _D("create memory cgroup for %s, ret = %d", name, ret); + /* create sub cgroups */ + gslist_for_each_item(iter, memcg[i]->cgroups) { + mi = (struct memcg_info_t *) + iter->data; + name = mi->name; + ret = make_cgroup_subdir(NULL, name, NULL); + _D("make cgroup subdir for %s, ret = %d", name, ret); + } + } - if (process_info && !(process_info->type & PROC_LARGE_HEAP)) - lowmem_dynamic_process_killer(DYNAMIC_KILL_LUNCH); return ret; } @@ -1882,29 +1922,21 @@ int lowmem_init(void) { int ret = RESOURCED_ERROR_NONE; - ret = create_foreground_memcg(); - - if (ret) { - _E("create foreground memcgs failed"); - return ret; - } get_total_memory(); - init_thresholds(); + + init_memcg_params(); + setup_memcg_params(); config_parse(MEM_CONF_FILE, load_mem_config, NULL); + create_memcgs(); + write_memcg_params(); + ret = oom_thread_create(); if (ret) { _E("oom thread create failed\n"); return ret; } - /* set default memcg value */ - ret = init_memcg(); - if (ret) { - _E("memory cgroup init failed"); - return ret; - } - /* register threshold and event fd */ ret = setup_eventfd(); if (ret) { @@ -1913,11 +1945,21 @@ int lowmem_init(void) } lowmem_dbus_init(); - register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_app_launch_cb); return ret; } +static int lowmem_exit(void) +{ + int i; + for (i = 0; i < MEMCG_MAX; i++) { + g_slist_free_full(memcg[i]->cgroups, free); + free(memcg[i]->info); + free(memcg[i]); + } + return RESOURCED_ERROR_NONE; +} + static int resourced_memory_control(void *data) { int ret = RESOURCED_ERROR_NONE; @@ -1941,14 +1983,12 @@ static int resourced_memory_control(void *data) static int resourced_memory_init(void *data) { lowmem_ops = &memory_modules_ops; - return lowmem_init(); } static int resourced_memory_finalize(void *data) { - unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_app_launch_cb); - return RESOURCED_ERROR_NONE; + return lowmem_exit(); } int lowmem_control(enum lowmem_control_type type, unsigned long *args) diff --git a/src/memps/CMakeLists.txt b/src/memps/CMakeLists.txt index 80bfc202..07cbe250 100644 --- a/src/memps/CMakeLists.txt +++ b/src/memps/CMakeLists.txt @@ -1,7 +1,7 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -fPIC -Wall -Werror") -SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") +SET(CMAKE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -pie") IF("${ARCH}" STREQUAL "arm") ADD_DEFINITIONS("-DTARGET") diff --git a/src/memps/memps.c b/src/memps/memps.c index bc7b7527..ae7edecd 100644 --- a/src/memps/memps.c +++ b/src/memps/memps.c @@ -1053,7 +1053,11 @@ void check_kernel_version(void) char str[3]; int sub_version; pch = strstr(buf.release, "."); + if (!pch) + return; + strncpy(str, pch+1, 2); + str[2] = '\0'; sub_version = atoi(str); if (sub_version >= 10) @@ -1065,7 +1069,7 @@ void check_kernel_version(void) ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages, Swap, KernelPageSize, MMUPageSize, Locked */ - } else { + } else { ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize, MMUPageSize */ } diff --git a/src/network/500.resourced-datausage.patch.sh b/src/network/500.resourced-datausage.patch.sh new file mode 100644 index 00000000..43f25ec9 --- /dev/null +++ b/src/network/500.resourced-datausage.patch.sh @@ -0,0 +1,19 @@ +sqlite3 /opt/usr/dbspace/.resourced-datausage.db " +PRAGMA journal_mode=PERSIST; +DROP TABLE IF EXISTS fota; +ALTER TABLE restrictions RENAME to fota; +CREATE TABLE IF NOT EXISTS restrictions ( +binpath TEXT, +rcv_limit BIGINT, +send_limit BIGINT, +iftype INT, +rst_state INT, +quota_id INT, +roaming INT, +reserved TEXT, +ifname TEXT, +PRIMARY KEY (binpath, iftype, ifname, quota_id) +); +INSERT INTO restrictions select * from fota; +DROP TABLE IF EXISTS fota; +" diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index c02da8d8..792223db 100755 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,13 +1,27 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(PROJECT ${RESOURCED}) +PROJECT(${PROJECT}) + IF("${NETWORK_MODULE}" STREQUAL "OFF") - MESSAGE("DataUsage is disbaled") + INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR} + ${INCLUDE_COMMON_DIR}) + SET(SOURCES ${NETWORK_SOURCE_DIR}/join-dummy.c) + ADD_LIBRARY(${PROJECT} SHARED ${SOURCES}) + TARGET_LINK_LIBRARIES(${PROJECT} ${shared_pkgs_LDFLAGS}) + + SET_TARGET_PROPERTIES(${PROJECT} + PROPERTIES + VERSION ${FULLVER} + SOVERSION ${MAJORVER} + CLEAN_DIRECT_OUTPUT 1 + ) + + INSTALL(TARGETS ${PROJECT} DESTINATION ${LIB_INSTALL_DIR}) + RETURN() ENDIF() -SET(PROJECT ${RESOURCED}) -PROJECT(${PROJECT}) - INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR} ${NETWORK_SOURCE_DIR}/include ${RESOURCED_INCLUDEDIR} @@ -19,13 +33,10 @@ SET (REQUIRES_LIST dlog vconf ecore edbus + openssl + tapi ) -IF("${TETHERING_FEATURE}" STREQUAL "ON") - SET(REQUIRES_LIST ${REQUIRES_LIST} - capi-network-connection) -ENDIF() - INCLUDE(FindPkgConfig) pkg_check_modules(pkgs_${NETWORK} REQUIRED ${REQUIRES_LIST}) @@ -62,12 +73,6 @@ SET(storage_SOURCES ${NETWORK_SOURCE_DIR}/datausage-quota-processing.c #storage uses it ${NETWORK_SOURCE_DIR}/storage.c) -SET(protocol-info_HEADERS - ${NETWORK_SOURCE_DIR}/include/protocol-info.h) - -SET(protocol-info_SOURCES - ${NETWORK_SOURCE_DIR}/protocol-info.c) - SET(quota_HEADERS ${NETWORK_SOURCE_DIR}/include/datausage-quota-processing.h) @@ -76,9 +81,6 @@ SET(quota_SOURCES ${NETWORK_SOURCE_DIR}/datausage-quota.c ) -SET(app-stat_HEADERS - ${INCLUDE_COMMON_DIR}/app-stat.h) - SET(app-stat_SOURCES ${NETWORK_SOURCE_DIR}/app-stat.c) @@ -87,33 +89,31 @@ SET(net-iface_HEADERS SET(net-iface_SOURCES ${NETWORK_SOURCE_DIR}/iface.c + ${COMMON_SOURCE_DIR}/config-parser.c ) -SET(roaming_HEADERS - ${NETWORK_SOURCE_DIR}/include/roaming.h) - -SET(roaming_SOURCES - ${NETWORK_SOURCE_DIR}/dummy_roaming.c) +SET(telephony_HEADERS + ${NETWORK_SOURCE_DIR}/include/telephony.h) -ADD_LIBRARY(protocol-info STATIC - ${protocol-info_SOURCES} ${protocol-info_HEADERS}) +SET(telephony_SOURCES + ${NETWORK_SOURCE_DIR}/telephony.c) ADD_LIBRARY(storage STATIC ${storage_SOURCES} ${storage_HEADERS} ${quota_SOURCES} ${quota_HEADERS} ) -TARGET_LINK_LIBRARIES(storage protocol-info ${pkgs_${NETWORK}_LDFLAGS}) +TARGET_LINK_LIBRARIES(storage ${pkgs_${NETWORK}_LDFLAGS}) ADD_LIBRARY(app-stat STATIC - ${app-stat_SOURCES} ${app-stat_HEADERS}) + ${app-stat_SOURCES}) TARGET_LINK_LIBRARIES(app-stat net-cls) ADD_LIBRARY(net-iface STATIC ${net-iface_SOURCES}) -ADD_LIBRARY(roaming STATIC - ${roaming_SOURCES}) -TARGET_LINK_LIBRARIES(roaming ${pkgs_${NETWORK}_LDFLAGS}) +ADD_LIBRARY(telephony STATIC + ${telephony_SOURCES}) +TARGET_LINK_LIBRARIES(telephony ${pkgs_${NETWORK}_LDFLAGS}) SET(net-cls_HEADERS ${NETWORK_SOURCE_DIR}/include/net-cls-cgroup.h) @@ -175,17 +175,13 @@ ENDFOREACH(flag) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIC") -INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR} - ${NETWORK_SOURCE_DIR}/include - ${RESOURCED_SOURCE_DIR}) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") ADD_LIBRARY(${PROJECT} SHARED ${${PROJECT}_HEADERS} ${${PROJECT}_SOURCES}) -TARGET_LINK_LIBRARIES(${PROJECT} roaming settings net-cls +TARGET_LINK_LIBRARIES(${PROJECT} telephony settings net-cls ${shared_pkgs_LDFLAGS}) SET_TARGET_PROPERTIES(${PROJECT} @@ -199,30 +195,12 @@ ADD_EXECUTABLE(datausagetool ${UTILS_SOURCE_DIR}/datausage-tool.c) TARGET_LINK_LIBRARIES(datausagetool ${PROJECT} ${COMMON_SOURCE_DIR}/config-parser.c - net-iface) + # ${NETWORK_SOURCE_DIR}/app-stat.c + net-iface + ) INSTALL(TARGETS datausagetool DESTINATION /usr/bin) INSTALL(TARGETS ${PROJECT} DESTINATION ${LIB_INSTALL_DIR}) INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/data_usage.h DESTINATION include/system) #### lib-resourced #### - -#### rd-network #### -SET(SOURCES - ${NETWORK_SOURCE_DIR}/network-dummy.c) - -ADD_LIBRARY(${NETWORK} SHARED ${SOURCES}) -ADD_DEPENDENCIES(${NETWORK} ${PROJECT}) -TARGET_LINK_LIBRARIES(${NETWORK} ${${NETWORK}_LDFLAGS} -L${LIBRARY_OUTPUT_PATH} -l${PROJECT}) - -SET_TARGET_PROPERTIES(${NETWORK} - PROPERTIES - VERSION ${FULLVER} - SOVERSION ${MAJORVER} - CLEAN_DIRECT_OUTPUT 1 -) - -INSTALL(TARGETS ${NETWORK} DESTINATION ${LIB_INSTALL_DIR}) -INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/rd-network.h DESTINATION include/system) - -#### rd-network #### diff --git a/src/network/app-stat.c b/src/network/app-stat.c index 35bd8a84..9c35d950 100644 --- a/src/network/app-stat.c +++ b/src/network/app-stat.c @@ -24,16 +24,24 @@ * @desc application statistics entity helper functions */ +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include "app-stat.h" +#include "counter.h" +#include "datausage-common.h" #include "net-cls-cgroup.h" #include "iface.h" #include "macro.h" -#include "roaming.h" +#include "telephony.h" #include "trace.h" +#ifdef CONFIG_DATAUSAGE_NFACCT +#include "nfacct-rule.h" +#else +#include "genl.h" +#endif static void free_app(gpointer data) { @@ -52,22 +60,23 @@ static gint compare_classid(gconstpointer a, gconstpointer b, { const struct classid_iftype_key *a_key = (struct classid_iftype_key*)a; const struct classid_iftype_key *b_key = (struct classid_iftype_key*)b; - gint ret = 0; + gint ret = a_key->classid - b_key->classid; - ret = a_key->classid - b_key->classid; - if (!ret) + if (ret) return ret; ret = a_key->iftype - b_key->iftype; - if (!ret) + if (ret) return ret; - return strcmp(a_key->ifname, b_key->ifname); -} + ret = (a_key->ifname && b_key->ifname) ? + strcmp(a_key->ifname, b_key->ifname) : 0; -static void free_stat(gpointer data) -{ - free(data); + if (ret) + return ret; + + return (a_key->imsi && b_key->imsi) ? + strcmp(a_key->imsi, b_key->imsi) : 0; } struct application_stat_tree *create_app_stat_tree(void) @@ -84,7 +93,7 @@ struct application_stat_tree *create_app_stat_tree(void) app_stat_tree->tree = (GTree *)g_tree_new_full(compare_classid, - NULL, free_stat, + NULL, free, free_app); app_stat_tree->last_touch_time = time(0); ret = pthread_rwlock_init(&app_stat_tree->guard, NULL); @@ -111,16 +120,6 @@ void nulify_app_stat_tree(struct application_stat_tree **app_stat_tree) *app_stat_tree = NULL; } -traffic_stat_tree *create_traffic_stat_tree(void) -{ - return g_tree_new_full(compare_classid, NULL, NULL, free_stat); -} - -void free_traffic_stat_tree(traffic_stat_tree *tree) -{ - g_tree_destroy((GTree *) tree); -} - static gboolean set_app_id(gpointer key, gpointer value, void __attribute__((__unused__)) *data) { @@ -139,81 +138,165 @@ static inline void identify_application( g_tree_foreach(app_stat_tree->tree, (GTraverseFunc)set_app_id, NULL); } -static gboolean fill_incomming(gpointer key, - gpointer value, gpointer data) +#ifdef CONFIG_DATAUSAGE_NFACCT + +static void fill_nfacct_counter(struct nfacct_rule *counter, uint64_t bytes) { + struct classid_iftype_key *key; + struct classid_iftype_key search_key = {0}; + struct counter_arg *carg = counter->carg; struct application_stat_tree *app_tree = - (struct application_stat_tree *)data; - struct traffic_stat *in_event = (struct traffic_stat *)value; - + (struct application_stat_tree *)carg->result; struct application_stat *app_stat = NULL; - if (!is_allowed_ifindex(in_event->ifindex)) - return FALSE; + + search_key.classid = counter->classid; + search_key.iftype = counter->iftype; + STRING_SAVE_COPY(search_key.ifname, counter->ifname); + search_key.imsi = counter->iftype == RESOURCED_IFACE_DATACALL ? + get_current_modem_imsi() : ""; /* we'll not free it */ app_stat = (struct application_stat *) - g_tree_lookup((GTree *)app_tree->tree, key); - if (app_stat) - app_stat->rcv_count += in_event->bytes; - else { + g_tree_lookup((GTree *)app_tree->tree, &search_key); + + if (!app_stat) { + key = g_new(struct classid_iftype_key, 1); + + if (!key) { + _D("g_new alloc error\n"); + return; + } + memcpy(key, &search_key, sizeof(struct classid_iftype_key)); + STRING_SAVE_COPY(key->ifname, search_key.ifname); + app_stat = g_new(struct application_stat, 1); + if (!app_stat) { + _D("g_new alloc error\n"); + g_free((gpointer)key); + return; + } memset(app_stat, 0, sizeof(struct application_stat)); - app_stat->rcv_count = in_event->bytes; - g_tree_insert((GTree *)app_tree->tree, key, app_stat); + g_tree_insert((GTree *)app_tree->tree, (gpointer)key, (gpointer)app_stat); + _D("new app stat for classid %u\n", counter->classid); + } else { + _D("app stat for classid %d found in tree", search_key.classid); + _D("app stats app id %s", app_stat->application_id); + _D("counter intend %d", counter->intend); } - app_stat->delta_rcv += in_event->bytes; - /*only for debug purpose*/ - if (!app_stat->ifindex) - app_stat->ifindex = in_event->ifindex; + if (counter->iotype == NFACCT_COUNTER_IN) { + app_stat->delta_rcv += bytes; /* += because we could update + counters several times before + flush it */ + app_stat->rcv_count += bytes; /* for different update/flush interval + in quota processing, + quota nulifies it and flush operation + as well, so 2 counters */ + } else if (counter->iotype == NFACCT_COUNTER_OUT) { + app_stat->delta_rcv += bytes; + app_stat->rcv_count += bytes; + } - app_stat->is_roaming = get_roaming(); - return FALSE; + app_stat->is_roaming = get_current_roaming(); + if (!app_stat->application_id) + app_stat->application_id = get_app_id_by_classid(counter->classid, false); + app_stat->ground = get_app_ground(counter); } -static gboolean fill_outgoing(gpointer key, - gpointer value, gpointer data) +static void fill_nfacct_restriction(struct nfacct_rule *counter, uint64_t bytes) { - struct application_stat_tree *app_tree = - (struct application_stat_tree *)data; - struct traffic_stat *out_event = (struct traffic_stat *)value; - - struct application_stat *app_stat = (struct application_stat *) - g_tree_lookup((GTree *)app_tree->tree, key); - if (app_stat) - app_stat->snd_count += out_event->bytes; - else { - app_stat = g_new(struct application_stat, 1); - memset(app_stat, 0, sizeof(struct application_stat)); - app_stat->snd_count = out_event->bytes; - g_tree_insert((GTree *)app_tree->tree, key, app_stat); + /* update db from here ? */ + _D("byte for restriction %" PRIu64 " ", bytes); + update_counter_quota_value(counter, bytes); +} + +void fill_nfacct_result(char *cnt_name, uint64_t bytes, + struct counter_arg *carg) +{ + struct nfacct_rule counter = { + .carg = carg, + .name = {0}, + .ifname = {0}, + 0, }; + + _D("cnt_name %s", cnt_name); + + if (!recreate_counter_by_name(cnt_name, &counter)) { + _E("Can't parse counter name %s", cnt_name); + return; } - app_stat->delta_snd += out_event->bytes; - if (!app_stat->ifindex) - app_stat->ifindex = out_event->ifindex; + _D("classid %u, iftype %u, iotype %d, intend %d, ifname %s, bytes %lu", + counter.classid, counter.iftype, counter.iotype, counter.intend, counter.ifname, bytes); - if (!app_stat->is_roaming) - app_stat->is_roaming = get_roaming(); - return FALSE; + if (counter.iotype == NFACCT_COUNTER_UNKNOWN) { + _D("Counter type is not supported!"); + return; + } + if (counter.intend == NFACCT_COUNTER || + counter.intend == NFACCT_TETH_COUNTER) { + return fill_nfacct_counter(&counter, bytes); + } else if (counter.intend == NFACCT_BLOCK) + return fill_nfacct_restriction(&counter, bytes); } +#else +API void fill_app_stat_result(int ifindex, int classid, uint64_t bytes, int iotype, + struct counter_arg *carg) +{ + struct classid_iftype_key *key; + struct classid_iftype_key search_key = {0}; + char *ifname; + struct application_stat_tree *app_tree = + (struct application_stat_tree *)carg->result; + struct application_stat *app_stat = NULL; -static void fill_result(traffic_stat_tree *tree_in, - traffic_stat_tree *tree_out, - struct application_stat_tree *result) -{ + search_key.classid = classid; + search_key.iftype = get_iftype(ifindex); + ifname = get_iftype_name(search_key.iftype); + STRING_SAVE_COPY(search_key.ifname, ifname); + search_key.imsi = search_key.iftype == RESOURCED_IFACE_DATACALL ? + get_current_modem_imsi() : ""; /* we'll not free it */ - g_tree_foreach(tree_in, (GTraverseFunc)fill_incomming, result); - g_tree_foreach(tree_out, (GTraverseFunc)fill_outgoing, result); -} + app_stat = (struct application_stat *) + g_tree_lookup((GTree *)app_tree->tree, &search_key); -resourced_ret_c prepare_application_stat(traffic_stat_tree *tree_in, - traffic_stat_tree *tree_out, - struct application_stat_tree *result, - volatile struct daemon_opts *opts) -{ - fill_result(tree_in, tree_out, result); - identify_application(result); + if (!app_stat) { + key = g_new(struct classid_iftype_key, 1); + + if (!key) { + _D("g_new alloc error\n"); + return; + } + memcpy(key, &search_key, sizeof(struct classid_iftype_key)); + STRING_SAVE_COPY(key->ifname, search_key.ifname); + + app_stat = g_new(struct application_stat, 1); + if (!app_stat) { + _D("g_new alloc error\n"); + g_free((gpointer)key); + return; + } + memset(app_stat, 0, sizeof(struct application_stat)); + g_tree_insert((GTree *)app_tree->tree, (gpointer)key, (gpointer)app_stat); + _D("new app stat for classid %u\n", classid); + } + + if (iotype == TRAF_STAT_C_GET_CONN_IN) { + app_stat->delta_rcv += bytes; /* += because we could update + counters several times before + flush it */ + app_stat->rcv_count += bytes; /* for different update/flush interval + in quota processing, + quota nulifies it and flush operation + as well, so 2 counters */ + } else if (iotype == TRAF_STAT_C_GET_PID_OUT) { + app_stat->delta_snd += bytes; + app_stat->snd_count += bytes; + } + + app_stat->is_roaming = get_current_roaming(); + if (!app_stat->application_id) + app_stat->application_id = get_app_id_by_classid(classid, false); - return RESOURCED_ERROR_NONE; } +#endif /* CONFIG_DATAUSAGE_NFACCT */ diff --git a/src/network/counter-process.c b/src/network/counter-process.c index f8abd53a..20a03fd4 100644 --- a/src/network/counter-process.c +++ b/src/network/counter-process.c @@ -43,7 +43,7 @@ #include "module-data.h" #include "notification.h" #include "resourced.h" -#include "roaming.h" +#include "telephony.h" #include "storage.h" #include "trace.h" #include "transmission.h" @@ -62,10 +62,10 @@ static char *null_str = "(null)"; #define INSERT_QUERY "REPLACE INTO quotas " \ "(binpath, sent_quota, rcv_quota, " \ "snd_warning_threshold, rcv_warning_threshold, time_period, " \ - "start_time, iftype, roaming) " \ - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);" + "start_time, iftype, roaming, imsi, ground) " \ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" #define REMOVE_QUOTA "DELETE FROM quotas WHERE binpath=? AND iftype=? " \ - " AND roaming=?" + " AND roaming=? AND imsi=? AND ground=?" #define QUOTA_CEILING_VALUE 10737418220 @@ -103,7 +103,13 @@ static bool check_net_blocked(sig_atomic_t state) #ifdef CONFIG_DATAUSAGE_NFACCT static Eina_Bool send_counter_request(struct counter_arg *carg) { - return nfacct_send_get(carg) == RESOURCED_ERROR_NONE ? + resourced_ret_c ret; + if (CHECK_BIT(carg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE)) + ret = nfacct_send_get_all(carg); + else + ret = nfacct_send_get_counters(carg, NULL); + + return ret == RESOURCED_ERROR_NONE ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL; } @@ -129,6 +135,10 @@ static void populate_counters(char *cnt_name, return; } + if (counter.intend == NFACCT_TETH_COUNTER) { + _D("no need to populate already created counters"); + return; + } counter.carg = carg; strcpy(counter.name, cnt_name); jump = get_counter_jump(counter.intend); @@ -138,55 +148,17 @@ static void populate_counters(char *cnt_name, produce_net_rule(&counter, 0, 0, NFACCT_ACTION_APPEND, jump, counter.iotype); - - keep_counter(&counter); } -static void populate_traf_stat_list(char *cnt_name, uint64_t bytes, - struct counter_arg *carg) +static void finalize_response(const char *cnt_name, struct counter_arg *carg) { - struct traffic_stat *to_insert; - struct classid_iftype_key *key; - struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, }; - traffic_stat_tree *tree = NULL; + struct nfacct_rule counter = { .carg = carg, 0 }; +#ifdef DEBUG_ENABLED _D("cnt_name %s", cnt_name); - - if (!recreate_counter_by_name(cnt_name, &counter)) { - _E("Can't parse counter name %s", cnt_name); - return; - } - - _D("classid %u, iftype %u, iotype %d, intend %d, ifname %s, bytes %lu", - counter.classid, counter.iftype, counter.iotype, counter.intend, counter.ifname, bytes); - - if (counter.iotype == NFACCT_COUNTER_UNKNOWN || - counter.intend != NFACCT_COUNTER) { - _E("Counter type is not supported!"); - return; - } - - tree = counter.iotype == NFACCT_COUNTER_IN ? carg->in_tree : carg->out_tree; - to_insert = g_new(struct traffic_stat, 1); - if (!to_insert) { - _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat)); - return; - } - - key = g_new(struct classid_iftype_key, 1); - - if (!key) { - _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key)); - g_free((gpointer)to_insert); - return; - } - - to_insert->bytes = bytes; - /*to_insert->ifindex = cur->ifindex;*/ - key->classid = counter.classid; - key->iftype = counter.iftype; - STRING_SAVE_COPY(key->ifname, counter.ifname); - g_tree_insert((GTree *) tree, (gpointer)key, to_insert); +#endif + recreate_counter_by_name((char *)cnt_name, &counter); + finalize_counter(&counter); } static int fill_counters(struct rtattr *attr_list[__NFACCT_MAX], @@ -203,8 +175,11 @@ static int fill_counters(struct rtattr *attr_list[__NFACCT_MAX], /* TODO: optimize at kernel level, kernel should not send counter * in case of 0 bytes, it's necessary to introduce new NFACCT_* * command */ - if (bytes) - populate_traf_stat_list(cnt_name, bytes, carg); + if (bytes) { + ++carg->serialized_counters; + fill_nfacct_result(cnt_name, bytes, carg); + } + finalize_response(cnt_name, carg); } return 0; @@ -240,23 +215,23 @@ static Eina_Bool send_counter_request(struct counter_arg *carg) static Eina_Bool _counter_func_cb(void *user_data) { struct counter_arg *carg = (struct counter_arg *)user_data; + Eina_Bool cb_result = ECORE_CALLBACK_RENEW; if (check_net_blocked(carg->opts->state)) { ecore_timer_freeze(carg->ecore_timer); return ECORE_CALLBACK_RENEW; } - if (!(carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)) { - /* Here we just sent command, - * answer we receiving in another callback, send_command uses - * return value the same as sendto - */ + /* Here we just sent command, + * answer we receiving in another callback, send_command uses + * return value the same as sendto */ + cb_result = send_counter_request(carg); - return send_counter_request(carg); - } + /* In case of FORCIBLY_QUIT_STATE we just send one request and exit */ + if (CHECK_BIT(carg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE)) + return ECORE_CALLBACK_CANCEL; - close(carg->sock); - return ECORE_CALLBACK_CANCEL; + return cb_result; } static dbus_bool_t deserialize_restriction( @@ -317,8 +292,9 @@ static DBusMessage *edbus_process_restriction(E_DBus_Object *obj, goto out; } + /* restriction is not imsi based */ dbus_ret = proc_keep_restriction(appid, NONE_QUOTA_ID, &rest, - rst_type); + rst_type, false); out: dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &dbus_ret); @@ -336,9 +312,10 @@ static DBusMessage *edbus_update_counters(E_DBus_Object *obj, DBusMessage *msg) "Method is not supported"); if (m_data != NULL && m_data->carg != NULL) { - if (!(m_data->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)) - m_data->carg->opts->state |= - RESOURCED_FORCIBLY_FLUSH_STATE; + if (!(CHECK_BIT(m_data->carg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE))) { + SET_BIT(m_data->carg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE); + SET_BIT(m_data->carg->opts->state, RESOURCED_UPDATE_REQUESTED); + } /* postpone periodic update on one minute */ reschedule_count_timer(m_data->carg, COUNTER_UPDATE_PERIOD); @@ -349,18 +326,6 @@ static DBusMessage *edbus_update_counters(E_DBus_Object *obj, DBusMessage *msg) return reply; } -struct serialization_quota { - int time_period; - int64_t snd_quota; - int64_t rcv_quota; - int snd_warning_threshold; - int rcv_warning_threshold; - resourced_state_t quota_type; - resourced_iface_type iftype; - time_t start_time; - resourced_roaming_type roaming_type; -}; - static inline int _get_threshold_part(int time_period) { if (time_period < RESOURCED_PERIOD_DAY) @@ -413,16 +378,42 @@ static dbus_bool_t deserialize_quota( DBUS_TYPE_INT32, "a->iftype, DBUS_TYPE_INT32, "a->start_time, DBUS_TYPE_INT32, "a->roaming_type, + DBUS_TYPE_STRING, "a->imsi_hash, DBUS_TYPE_INVALID); if (ret == FALSE) { _E("Can't deserialize set quota message ![%s:%s]\n", err.name, err.message); + goto release; } - dbus_error_free(&err); + if (!quota->start_time) { + _E("Start time wasn't specified!\n"); + ret = FALSE; + goto release; + } - quota->iftype = (quota->iftype == RESOURCED_IFACE_UNKNOWN) ? - RESOURCED_IFACE_ALL : quota->iftype; + if (!quota->time_period) { + _E("Time period wasn't specified!\n"); + ret = FALSE; + goto release; + } + + if(quota->iftype <= RESOURCED_IFACE_UNKNOWN || + quota->iftype >= RESOURCED_IFACE_LAST_ELEM) { + _E("Unknown network interface is inacceptable!"); + ret = FALSE; + goto release; + } + + if (quota->roaming_type < RESOURCED_ROAMING_UNKNOWN || + quota->roaming_type >= RESOURCED_ROAMING_LAST_ELEM || + (quota->roaming_type == RESOURCED_ROAMING_UNKNOWN && + quota->iftype == RESOURCED_IFACE_DATACALL)) + { + _E("Bad roaming!"); + ret = FALSE; + goto release; + } quota->snd_warning_threshold = _evaluate_warning_threshold( quota->snd_quota, quota->time_period, @@ -430,13 +421,19 @@ static dbus_bool_t deserialize_quota( quota->rcv_warning_threshold = _evaluate_warning_threshold( quota->rcv_quota, quota->time_period, quota->rcv_warning_threshold); + _D("calculated snd_warning_threshold %d", quota->snd_warning_threshold); + _D("calculated rcv_warning_threshold %d", quota->rcv_warning_threshold); + +release: -return ret; + dbus_error_free(&err); + return ret; } static dbus_bool_t deserialize_remove_quota( DBusMessage *msg, char **appid, - resourced_iface_type *iftype, resourced_roaming_type *roaming) + resourced_iface_type *iftype, resourced_roaming_type *roaming, + char **imsi, resourced_state_t *ground) { DBusError err; dbus_error_init(&err); @@ -446,6 +443,8 @@ static dbus_bool_t deserialize_remove_quota( DBUS_TYPE_STRING, appid, DBUS_TYPE_INT32, iftype, DBUS_TYPE_INT32, roaming, + DBUS_TYPE_STRING, imsi, + DBUS_TYPE_INT32, ground, DBUS_TYPE_INVALID); if (ret == FALSE) { _E("Can't deserialize remove quota message! [%s:%s]\n", @@ -504,7 +503,7 @@ static int init_datausage_quota_remove(sqlite3 *db) if (datausage_quota_remove) return SQLITE_OK; - rc = sqlite3_prepare_v2(db, REMOVE_QUOTA, -1, + rc = sqlite3_prepare_v2(db, REMOVE_QUOTA, -1, &datausage_quota_remove, NULL); if (rc != SQLITE_OK) { _E("can not prepare datausage_quota_remove"); @@ -517,7 +516,8 @@ static int init_datausage_quota_remove(sqlite3 *db) } static resourced_ret_c remove_quota(const char *app_id, - resourced_iface_type iftype, resourced_roaming_type roaming) + resourced_iface_type iftype, resourced_roaming_type roaming, + char *imsi_hash, const resourced_state_t ground) { resourced_ret_c error_code = RESOURCED_ERROR_NONE; libresourced_db_initialize_once(); @@ -546,19 +546,36 @@ static resourced_ret_c remove_quota(const char *app_id, if (sqlite3_bind_int(datausage_quota_remove, 3, roaming) != SQLITE_OK) { - _E("Can not bind iftype:%d for preparing statement", + _E("Can not bind roaming:%d for preparing statement", roaming); error_code = RESOURCED_ERROR_DB_FAILED; goto out; } + if (sqlite3_bind_text(datausage_quota_remove, 4, imsi_hash, -1, SQLITE_STATIC) + != SQLITE_OK) { + _E("Can not bind subscriber_id:%s for preparing statement", + imsi_hash); + error_code = RESOURCED_ERROR_DB_FAILED; + goto out; + } + + if (sqlite3_bind_int(datausage_quota_remove, 5, ground) + != SQLITE_OK) { + _E("Can not bind ground:%d for preparing statement", + ground); + error_code = RESOURCED_ERROR_DB_FAILED; + goto out; + } + if (sqlite3_step(datausage_quota_remove) != SQLITE_DONE) { _E("failed to remove record"); error_code = RESOURCED_ERROR_DB_FAILED; goto out; } - restriction_set_status(RESTRICTION_STATE_UNSET); + if (!check_event_in_current_modem(imsi_hash, iftype)) + check_and_clear_all_noti(); _SD("quota for app %s removed", app_id); @@ -570,11 +587,25 @@ out: static DBusMessage *edbus_remove_quota(E_DBus_Object *obj, DBusMessage *msg) { char *app_id = NULL; + char *imsi_hash = NULL; + int quota_id = 0; resourced_iface_type iftype; + resourced_state_t ground; resourced_ret_c ret = RESOURCED_ERROR_NONE; resourced_roaming_type roaming; DBusMessage *reply; DBusMessageIter iter; + struct shared_modules_data *m_data = get_shared_modules_data(); + struct counter_arg *carg; + + if (!m_data || !m_data->carg) { + _E("Not enough local parameters: modules data %p, counter arg %p", + m_data, m_data->carg); + ret = RESOURCED_ERROR_INVALID_PARAMETER; + goto remove_out; + } + + carg = m_data->carg; if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK, RESOURCED_NETWORK_REMOVE_QUOTA) == 0) { @@ -582,14 +613,28 @@ static DBusMessage *edbus_remove_quota(E_DBus_Object *obj, DBusMessage *msg) goto remove_out; } - if (deserialize_remove_quota(msg, &app_id, &iftype, &roaming) + if (deserialize_remove_quota(msg, &app_id, &iftype, &roaming, &imsi_hash, &ground) == FALSE) { ret = RESOURCED_ERROR_INVALID_PARAMETER; goto remove_out; } - ret = remove_quota(app_id, iftype, roaming); - update_quota_state(app_id, iftype, 0, 0, roaming); + ret = remove_quota(app_id, iftype, roaming, imsi_hash, ground); + if (check_quota_applied(app_id, iftype, roaming, imsi_hash, ground, "a_id)) { + ret = remove_restriction_local(app_id, iftype, quota_id, + imsi_hash, ground); + if (ret == RESOURCED_ERROR_NONE) + _D("Quota was applied and restriction was removed successfully."); + else + _E("Can't remove rules for restrictions"); + /* move background processes from BACKGROUND cgroup to + * their own cgroup */ + foreground_apps(carg); + } + + remove_quota_from_counting(app_id, iftype, roaming, imsi_hash); + clear_effective_quota(app_id, iftype, roaming, imsi_hash); + SET_BIT(carg->opts->state, RESOURCED_CHECK_QUOTA); remove_out: reply = dbus_message_new_method_return(msg); @@ -618,7 +663,7 @@ static int init_datausage_quota_insert(sqlite3 *db) } static resourced_ret_c store_quota(const char *app_id, - const struct serialization_quota *quota) + const struct serialization_quota *quota, int *quota_id) { resourced_ret_c error_code = RESOURCED_ERROR_NONE; @@ -631,7 +676,7 @@ static resourced_ret_c store_quota(const char *app_id, } if (sqlite3_bind_text(datausage_quota_insert, 1, app_id, -1, - SQLITE_STATIC) != SQLITE_OK) { + SQLITE_TRANSIENT) != SQLITE_OK) { _SE("Can not bind app_id: %s for prepearing statement: %s", app_id, sqlite3_errmsg(resourced_get_database())); error_code = RESOURCED_ERROR_DB_FAILED; @@ -696,8 +741,24 @@ static resourced_ret_c store_quota(const char *app_id, if (sqlite3_bind_int(datausage_quota_insert, 9, quota->roaming_type) != SQLITE_OK) { - _E("Can not bind start_time: %d for preparing statement", - quota->start_time); + _E("Can not bind roaming_type %d for preparing statement", + quota->roaming_type); + error_code = RESOURCED_ERROR_DB_FAILED; + goto out; + } + + if (sqlite3_bind_text(datausage_quota_insert, 10, + quota->imsi_hash, -1, SQLITE_TRANSIENT) != SQLITE_OK) { + _E("Can not bind subscriber_id: %s for preparing statement", + quota->imsi_hash); + error_code = RESOURCED_ERROR_DB_FAILED; + goto out; + } + + if (sqlite3_bind_int(datausage_quota_insert, 11, + quota->quota_type) != SQLITE_OK) { + _E("Can not bind quota_type %d for preparing statement", + quota->quota_type); error_code = RESOURCED_ERROR_DB_FAILED; goto out; } @@ -708,6 +769,8 @@ static resourced_ret_c store_quota(const char *app_id, error_code = RESOURCED_ERROR_DB_FAILED; goto out; } + + *quota_id = sqlite3_last_insert_rowid(resourced_get_database()); out: sqlite3_reset(datausage_quota_insert); return error_code; @@ -719,6 +782,7 @@ static DBusMessage *edbus_create_quota(E_DBus_Object *obj, DBusMessage *msg) DBusMessageIter iter; char *app_id = NULL; + int quota_id; struct serialization_quota quota; struct shared_modules_data *m_data = get_shared_modules_data(); struct counter_arg *carg; @@ -740,22 +804,24 @@ static DBusMessage *edbus_create_quota(E_DBus_Object *obj, DBusMessage *msg) goto update_out; } - deserialize_quota(msg, &app_id, "a); - ret = store_quota(app_id, "a); + if (deserialize_quota(msg, &app_id, "a) != TRUE) { + _E("Cant' deserialize quota"); + goto update_out; + } + ret = store_quota(app_id, "a, "a_id); if (ret != RESOURCED_ERROR_NONE) { _E("Can't store quota!"); goto update_out; } - update_quota_state(app_id, quota.iftype, quota.start_time, - quota.time_period, quota.roaming_type); + update_quota_state(app_id, quota_id, "a); ret_value_msg_if(!carg->opts, dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Counter args is not provided"), "Please provide valid argument!"); - carg->opts->is_update_quota = 1; + SET_BIT(carg->opts->state, RESOURCED_CHECK_QUOTA); reschedule_count_timer(carg, 0); _SD("Datausage quota changed"); @@ -823,9 +889,9 @@ static void prepare_response(struct get_stats_context *ctx) dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->interval->from); dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->interval->to); /* incoming bytes */ - dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.incoming_bytes); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->cnt.incoming_bytes); /* outgoing bytes */ - dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.outgoing_bytes); + dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->cnt.outgoing_bytes); dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->roaming); dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->hw_net_protocol_type); @@ -919,24 +985,91 @@ static inline char *_get_public_appid(const uint32_t classid) static bool need_flush_immediatelly(sig_atomic_t state) { - return state & RESOURCED_FORCIBLY_FLUSH_STATE || - state & RESOURCED_FORCIBLY_QUIT_STATE; + return CHECK_BIT(state, RESOURCED_FORCIBLY_FLUSH_STATE) || + CHECK_BIT(state, RESOURCED_FORCIBLY_QUIT_STATE); } -static Eina_Bool _store_and_free_result_cb(void *user_data) +static void free_restriction_info(void *data) +{ + resourced_restriction_info *info = (resourced_restriction_info *)data; + if (info->app_id) + free((char *)info->app_id); + if (info->ifname) + free((char *)info->ifname); +} + +static void store_restrictions(struct counter_arg *arg) +{ + GSList *rst_list = NULL, *iter = NULL; + + /* find restrictions in nf_cntrs tree, which were active */ + extract_restriction_list(arg, &rst_list); + + _D("Store restrictions!"); + + gslist_for_each_item(iter, rst_list) { + resourced_restriction_info *info = (resourced_restriction_info *)iter->data; + + /* when we moved to only one restriction counter, + * one of rcv_limit or send_limit value could be + * 0, !info->send_limit + */ + if (!info->rcv_limit) { + _D("Nothing to store"); + continue; + } + + /* roaming couldn't change without chaning of network interface + * undefined behavior here is roaming updated before interface down + * we could get here incorrect restriction and fail update it + * If it changes before need to keep roaming in nfacct_value + */ + update_restriction_db(info->app_id, info->iftype, + info->rcv_limit, info->send_limit, + info->rst_state, info->quota_id, + info->roaming, info->ifname); + } + + g_slist_free_full(rst_list, free_restriction_info); +} + +static bool check_flush_time(time_t flush_period, time_t last_time) +{ + time_t cur_time; + time(&cur_time); + return cur_time - last_time <= flush_period - 1; +} + +static Eina_Bool store_and_free_result_cb(void *user_data) { struct counter_arg *arg = (struct counter_arg *)user_data; + resourced_ret_c ret; ret_value_msg_if(!arg, ECORE_CALLBACK_CANCEL, "Please provide valid argument!"); - if (store_result(arg->result, need_flush_immediatelly(arg->opts->state) - ? 0 : arg->opts->flush_period)) { + if (check_flush_time(arg->opts->flush_period, arg->last_run_time) && + !need_flush_immediatelly(arg->opts->state)) + return ECORE_CALLBACK_CANCEL; + + /* It's dangerouse to store restriction every counting cycle, + * 1. we need to request it without reset cmd + * 2. we using nf_cntrs, and it case restriction wasn't modified, we + * couldn't determine it, and we'll store it + * 3. need to fix fill_restriction in answer_func_cb, + * to nulify nf_cntrs */ + if ((CHECK_BIT(arg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE) || + CHECK_BIT(arg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE)) && + !CHECK_BIT(arg->opts->state, RESOURCED_CHECK_QUOTA)) + store_restrictions(arg); + + ret = store_result(arg->result); + if (ret == RESOURCED_ERROR_NONE) { /*We still plan to use result outside, just remove and free elements */ g_tree_ref(arg->result->tree); free_app_stat_tree(arg->result); - if (arg->opts->state & RESOURCED_FORCIBLY_FLUSH_STATE) { - arg->opts->state &= ~RESOURCED_FORCIBLY_FLUSH_STATE; + if (CHECK_BIT(arg->opts->state, RESOURCED_UPDATE_REQUESTED)) { + UNSET_BIT(arg->opts->state, RESOURCED_UPDATE_REQUESTED); if (broadcast_edbus_signal( RESOURCED_PATH_NETWORK, RESOURCED_INTERFACE_NETWORK, @@ -947,14 +1080,40 @@ static Eina_Bool _store_and_free_result_cb(void *user_data) } arg->store_result_timer = NULL; + arg->serialized_counters = 0; + time(&(arg->last_run_time)); + + /* + * it's latest counter code for async operations, + * so here could be exit + */ + UNSET_BIT(arg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE); + UNSET_BIT(arg->opts->state, RESOURCED_CHECK_QUOTA); + + /* + * timer for quit is scheduled in sig term handler, but it has 1 sec delay, + * if we finished early we could quit here + */ + if (CHECK_BIT(arg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE)) + ecore_main_loop_quit(); return ECORE_CALLBACK_CANCEL; } -static void _store_and_free_result(struct counter_arg *arg) +static void store_and_free_result(struct counter_arg *arg) { + if (need_flush_immediatelly(arg->opts->state)) { + if (arg->store_result_timer) + ecore_timer_delay(arg->store_result_timer, + 0 - ecore_timer_pending_get(arg->store_result_timer)); + else + arg->store_result_timer = ecore_timer_add(0, + store_and_free_result_cb, arg); + return; + } + if (!arg->store_result_timer) - arg->store_result_timer = ecore_timer_add(STORE_DELAY_INTERVAL, - _store_and_free_result_cb, arg); + arg->store_result_timer = ecore_timer_add(0, + store_and_free_result_cb, arg); } static void _process_network_counter(struct nl_family_params *params) @@ -979,34 +1138,23 @@ static void _process_network_counter(struct nl_family_params *params) netlink->deserialize_answer(&(netlink->params)); - /* process only filled in/out or tethering traffic */ - if ((!g_tree_nnodes(params->carg->in_tree) || - !g_tree_nnodes(params->carg->out_tree)) && - params->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE) - return; - - pthread_rwlock_wrlock(¶ms->carg->result->guard); - ret = prepare_application_stat(params->carg->in_tree, - params->carg->out_tree, params->carg->result, - params->carg->opts); - pthread_rwlock_unlock(¶ms->carg->result->guard); - if (ret != RESOURCED_ERROR_NONE) { - _E("Failed to prepare application statistics!"); + if (!params->carg->serialized_counters && + !CHECK_BIT(params->carg->opts->state, + RESOURCED_CHECK_QUOTA)) { + /* it could be due, 0 value for all counters + * or due 0 payload in netlink response */ + _D("There is no serialized counters in response"); return; } - ret = process_quota(params->carg->result, params->carg->opts); + + ret = process_quota(params->carg); if (ret != 0) { _E("Failed to process quota!"); return; } - _store_and_free_result(params->carg); - - g_tree_ref(params->carg->out_tree); - free_traffic_stat_tree(params->carg->out_tree); - g_tree_ref(params->carg->in_tree); - free_traffic_stat_tree(params->carg->in_tree); + store_and_free_result(params->carg); } #ifdef CONFIG_DATAUSAGE_NFACCT @@ -1025,6 +1173,7 @@ static void _process_restriction(struct nl_family_params *cmd) char *app_id = NULL; resourced_iface_type iftype; resourced_restriction_info rst_info = {0,}; + data_usage_quota du_quota = {0}; resourced_ret_c ret; _D("Restriction notification"); @@ -1043,16 +1192,19 @@ static void _process_restriction(struct nl_family_params *cmd) ret_msg_if(ret != RESOURCED_ERROR_NONE, "Failed to get restriction info!"); + get_quota_by_id(rst_info.quota_id, &du_quota); + _D("quota rcv: %d, send: %d", du_quota.rcv_quota, du_quota.snd_quota); + if (notification_type == RESTRICTION_NOTI_C_ACTIVE) { if (rst_info.quota_id != NONE_QUOTA_ID) - send_restriction_notification(app_id); + send_restriction_notification(app_id, &du_quota); update_restriction_db(app_id, iftype, 0, 0, RESOURCED_RESTRICTION_ACTIVATED, - rst_info.quota_id, rst_info.roaming); + rst_info.quota_id, rst_info.roaming, rst_info.ifname); } else if (notification_type == RESTRICTION_NOTI_C_WARNING) { /* nested if due error message correctness */ if (rst_info.quota_id != NONE_QUOTA_ID) - send_restriction_warn_notification(app_id); + send_restriction_warn_notification(app_id, &du_quota); } else _E("Unkown restriction notification type"); } @@ -1170,7 +1322,6 @@ release_sock: } #endif /* CONFIG_DATAUSAGE_NFACCT */ - int resourced_init_counter_func(struct counter_arg *carg) { int error = 0; @@ -1186,8 +1337,6 @@ int resourced_init_counter_func(struct counter_arg *carg) "Couldn't init socket!"); carg->result = create_app_stat_tree(); - carg->in_tree = create_traffic_stat_tree(); - carg->out_tree = create_traffic_stat_tree(); #ifdef CONFIG_DATAUSAGE_NFACCT carg->nf_cntrs = create_nfacct_tree(); #endif /* CONFIG_DATAUSAGE_NFACCT */ @@ -1236,11 +1385,10 @@ static void finalize_quota_remove(void) void resourced_finalize_counter_func(struct counter_arg *carg) { ret_msg_if(carg == NULL, "Invalid counter argument\n"); - free_traffic_stat_tree(carg->out_tree); - free_traffic_stat_tree(carg->in_tree); nulify_app_stat_tree(&carg->result); ecore_main_fd_handler_del(carg->ecore_fd_handler); ecore_timer_del(carg->ecore_timer); + close(carg->sock); finalize_quota_insert(); finalize_quota_remove(); } diff --git a/src/network/counter.c b/src/network/counter.c index fd436a3e..16dfe278 100644 --- a/src/network/counter.c +++ b/src/network/counter.c @@ -41,6 +41,7 @@ struct counter_arg *init_counter_arg(struct daemon_opts *opts) result->pid = getpid(); #endif result->opts = opts; + result->serialized_counters = 0; return result; } diff --git a/src/network/datausage-common.c b/src/network/datausage-common.c index 697d5a68..f5e40dd3 100644 --- a/src/network/datausage-common.c +++ b/src/network/datausage-common.c @@ -23,13 +23,13 @@ * */ -#include "appid-helper.h" #include "config.h" #include "const.h" #include "counter-process.h" #include "counter.h" #include "cgroup.h" #include "datausage-restriction.h" +#include "db-guard.h" #include "generic-netlink.h" #include "net-cls-cgroup.h" #include "nl-helper.h" @@ -38,20 +38,24 @@ #include "daemon-options.h" #include "datausage-common.h" #include "datausage-quota.h" +#include "datausage-quota-processing.h" #include "datausage-vconf-callbacks.h" #include "iface-cb.h" #include "macro.h" #include "module-data.h" #include "module.h" #include "nfacct-rule.h" -#include "protocol-info.h" #include "resourced.h" #include "restriction-handler.h" -#include "roaming.h" +#include "telephony.h" +#include "tethering-restriction.h" #include "storage.h" #include "trace.h" #include <linux/rtnetlink.h> +#include <glib.h> +#include <inttypes.h> +#include <Ecore.h> #ifdef CONFIG_DATAUSAGE_NFACCT @@ -65,7 +69,8 @@ struct nfacct_key { u_int32_t classid; resourced_iface_type iftype; nfacct_rule_direction iotype; - char ifname[MAX_NAME_LENGTH]; + char ifname[MAX_IFACE_LENGTH]; + nfacct_rule_intend intend; }; enum nfacct_state { @@ -73,13 +78,36 @@ enum nfacct_state { NFACCT_STATE_DEACTIVATED, /* kernel counter was removed, but this counter is still active, and it will be required for network interface, when it will be activated */ + NFACCT_STATE_DEL_DELAYED, /* kernel counters is going to be removed */ }; +typedef enum { + NFACCT_FINAL_UNKNOWN, + NFACCT_FINAL_REMOVE = 1 << 0, +} nfacct_finalization; + struct nfacct_value { pid_t pid; - enum nfacct_state state; + enum nfacct_state state; /* used for distinguish incomplete counters, + when network interface not yet activated, + also for delayed counter deletion, + last is not good idea, I hope, to + rework it when it will be only one + iptable-restore call instead of + several*/ + resourced_ret_c(*iptables_rule)(struct nfacct_rule *counter); + nfacct_finalization fini; + /* restriction part */ + u_int64_t quota; + int quota_id; + resourced_roaming_type roaming; + resourced_restriction_state rst_state; + /* end restriction part */ + resourced_state_t ground; /* background/foreground state */ }; +static struct nfacct_value *lookup_counter(struct nfacct_rule *counter); + static nfacct_rule_jump get_jump_by_intend(struct nfacct_rule *counter) { if (counter->intend == NFACCT_WARN) @@ -120,15 +148,10 @@ static resourced_ret_c del_iptables_out(struct nfacct_rule *counter) #endif /* CONFIG_DATAUSAGE_NFACCT */ -static void resourced_roaming_cb_init(void) -{ - regist_roaming_cb(get_roaming_restriction_cb()); -} - static int app_launch_cb(void *data) { struct proc_status *p_data = (struct proc_status*)data; - int ret; + resourced_ret_c ret; ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL, "Please provide valid argument!"); ret = join_net_cls(p_data->appid, p_data->pid); @@ -139,45 +162,51 @@ static int app_launch_cb(void *data) #ifdef CONFIG_DATAUSAGE_NFACCT +static int app_launch_srv_cb(void *data) +{ + struct proc_status *p_data = (struct proc_status*)data; + resourced_ret_c ret; + bool background_quota; + ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL, + "Please provide valid argument!"); + ret = join_net_cls(p_data->appid, p_data->pid); + if (ret != RESOURCED_ERROR_NONE) + _D("Failed to start network counting."); + mark_background(p_data->appid); + + background_quota = get_background_quota(); + if (background_quota) + ret = place_pids_to_net_cgroup(p_data->pid, p_data->appid); + + return ret; +} + static int remove_each_counter( gpointer key, gpointer value, gpointer data) { struct nfacct_rule *counter = (struct nfacct_rule *)data; - resourced_iface_type iftype = *(resourced_iface_type *)value; - struct nfacct_key nf_key; + resourced_iface_type iftype = (resourced_iface_type)key; + char *ifname = (char *)value; + struct nfacct_value *nf_value; if (iftype == RESOURCED_IFACE_UNKNOWN) return FALSE; - nf_key.classid = counter->classid; - nf_key.iotype = counter->iotype; - counter->iftype = nf_key.iftype = iftype; + counter->iftype = iftype; + STRING_SAVE_COPY(counter->ifname, ifname); - generate_counter_name(counter); - counter->iptables_rule(counter); + nf_value = lookup_counter(counter); - /* remove from local tree */ -#ifdef DEBUG_ENABLED - { - gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key); - if (t) - _I("Element exists, remove it!"); - else - _D("Element doesn't exist!"); - } -#endif - - g_tree_remove(counter->carg->nf_cntrs, &nf_key); -#ifdef DEBUG_ENABLED - { - gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key); - if (t) - _E("Element wasn't removed!"); - } -#endif + ret_value_msg_if (!nf_value, FALSE, "Can't remove counter, due it's not in tree"); + SET_BIT(nf_value->fini, NFACCT_FINAL_REMOVE); + /* move it into _answer_func_cb */ + generate_counter_name(counter); + /* request update will be send in produce_net_rule, + * just by sending one get request, per name */ + counter->iptables_rule(counter); return FALSE; } @@ -188,19 +217,18 @@ static void remove_nfacct_counters_for_all_iface(u_int32_t classid, struct count .iotype = NFACCT_COUNTER_IN, .iptables_rule = del_iptables_in, .carg = carg, + .ifname = {0}, + 0, /* .name until we don't have iftype, * we couldn't get name */ }; - /* TODO rework for_each_ifindex to avoid cast, - * right now cast is necessary due for_each_ifindex directy pass - * given function into g_tree_foreach */ /* remove for ingress counter */ - for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter); + for_each_ifnames((ifnames_iterator)remove_each_counter, NULL, &counter); /* remove for engress counter */ counter.iotype = NFACCT_COUNTER_OUT; counter.iptables_rule = del_iptables_out; - for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter); + for_each_ifnames((ifnames_iterator)remove_each_counter, NULL, &counter); } struct match_nftree_context @@ -233,6 +261,147 @@ static u_int32_t get_classid_by_pid(struct counter_arg *carg, const pid_t pid) return ctx.classid; } +struct mark_context { + u_int32_t classid; + resourced_state_t ground; + int count; + struct counter_arg *carg; +}; + +static gboolean mark_ground_by_classid(gpointer key, gpointer value, + gpointer data) +{ + struct nfacct_key *nf_key = (struct nfacct_key *)key; + struct nfacct_value *nf_value = (struct nfacct_value *)value; + struct nfacct_rule rule = {.ifname = {0}, 0}; + struct mark_context *ctx = (struct mark_context *)data; + if (nf_key->classid != ctx->classid) + return FALSE; + + if (nf_value->ground != ctx->ground) { + strcpy(rule.ifname, nf_key->ifname); + rule.classid = nf_key->classid; + rule.iotype = nf_key->iotype; + rule.intend = nf_key->intend; + rule.carg = ctx->carg; + generate_counter_name(&rule); + nf_value->iptables_rule = 0; + nfacct_send_get(&rule); + nf_value->ground = ctx->ground; + } + + if (nf_value->state != NFACCT_STATE_DEACTIVATED) + ++ctx->count; + return FALSE; +} + +static int mark_ground_state(struct counter_arg *carg, u_int32_t classid, + resourced_state_t ground) +{ + struct mark_context ctx = { + .classid = classid, + .ground = ground, + .count = 0, + .carg = carg}; + /* find classid in tree */ + g_tree_foreach(carg->nf_cntrs, mark_ground_by_classid, &ctx); + return ctx.count; +} + +void mark_background(const char *app_id) +{ + struct shared_modules_data *m_data; + struct counter_arg *carg; + int nfacct_number = 0; + + u_int32_t classid = get_classid_by_app_id(app_id, false); + ret_msg_if(classid == RESOURCED_UNKNOWN_CLASSID, + "Unknown classid!"); + m_data = get_shared_modules_data(); + ret_msg_if(m_data == NULL, "Can't get module data!"); + + carg = m_data->carg; + ret_msg_if(carg == NULL, "Cant' get counter arg!"); + + nfacct_number = mark_ground_state(carg, classid, + RESOURCED_STATE_BACKGROUND); + if (!nfacct_number) + _D("There is no entry for %s in counter tree", app_id); +} + +static gboolean move_proc_background_cgroup(gpointer key, gpointer value, + gpointer data) +{ + struct nfacct_value *nf_value = (struct nfacct_value *)value; + struct nfacct_key *nf_key = (struct nfacct_key *)key; + resourced_state_t state = (resourced_state_t )data; + + if (nf_value->ground != RESOURCED_STATE_BACKGROUND || + nf_key->classid == RESOURCED_ALL_APP_CLASSID || + nf_key->intend != NFACCT_COUNTER) + return FALSE; + + /* move into background cgroup */ + if (state == RESOURCED_STATE_BACKGROUND) + place_pids_to_net_cgroup(nf_value->pid, RESOURCED_BACKGROUND_APP_NAME); + else { + char *app_id = get_app_id_by_classid(nf_key->classid, false); + if (app_id) + place_pids_to_net_cgroup(nf_value->pid, app_id); + } + return FALSE; +} + +void foreground_apps(struct counter_arg *carg) +{ + g_tree_foreach(carg->nf_cntrs, move_proc_background_cgroup, (void *)RESOURCED_STATE_FOREGROUND); +} + +void background_apps(struct counter_arg *carg) +{ + g_tree_foreach(carg->nf_cntrs, move_proc_background_cgroup, (void *)RESOURCED_STATE_BACKGROUND); +} + +static int app_resume_cb(void *data) +{ + struct proc_status *p_data = (struct proc_status*)data; + struct shared_modules_data *m_data; + struct counter_arg *carg; + u_int32_t classid; + resourced_ret_c ret = RESOURCED_ERROR_NONE; + int nfacct_number = 0; + bool background_quota = false; + + ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL, + "Please provide valid argument!"); + + m_data = get_shared_modules_data(); + ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL, + "Can't get module data!"); + + carg = m_data->carg; + ret_value_msg_if(carg == NULL, RESOURCED_ERROR_FAIL, + "Cant' get counter arg!"); + classid = get_classid_by_pid(carg, p_data->pid); + ret_value_msg_if(classid == RESOURCED_UNKNOWN_CLASSID, + RESOURCED_ERROR_FAIL, "No classid to terminate!"); + + /* find in tree nf_cntr and mark it as background */ + + nfacct_number = mark_ground_state(carg, classid, + RESOURCED_STATE_FOREGROUND); + background_quota = get_background_quota(); + if (!nfacct_number) { + ret = join_net_cls(p_data->appid, p_data->pid); + if (ret != RESOURCED_ERROR_NONE) + _D("Failed to start network counting."); + + } else if(background_quota) + place_pids_to_net_cgroup(p_data->pid, p_data->appid); + + return ret; +} + static int app_terminate_cb(void *data) { struct proc_status *p_data = (struct proc_status*)data; @@ -257,6 +426,53 @@ static int app_terminate_cb(void *data) return RESOURCED_ERROR_NONE; } +resourced_state_t get_app_ground(struct nfacct_rule *counter) +{ + struct nfacct_value *nf_value = lookup_counter(counter); + return nf_value ? nf_value->ground : RESOURCED_STATE_UNKNOWN; +} + +static int app_backgrnd_cb(void *data) +{ + struct proc_status *p_data = (struct proc_status*)data; + struct shared_modules_data *m_data; + struct counter_arg *carg; + u_int32_t classid; + int nfacct_number = 0; + bool background_quota = false; + ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL, + "Please provide valid argument!"); + + m_data = get_shared_modules_data(); + ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL, + "Can't get module data!"); + + carg = m_data->carg; + ret_value_msg_if(carg == NULL, RESOURCED_ERROR_FAIL, + "Cant' get counter arg!"); + classid = get_classid_by_pid(carg, p_data->pid); + ret_value_msg_if(classid == RESOURCED_UNKNOWN_CLASSID, + RESOURCED_ERROR_FAIL, "No classid to terminate!"); + + /* find in tree nf_cntr and mark it as background */ + + nfacct_number = mark_ground_state(carg, classid, + RESOURCED_STATE_BACKGROUND); + if (nfacct_number) { + background_quota = get_background_quota(); + /* if we have applied background quota, put current pid into + * background cgroup */ + if (background_quota) + place_pids_to_net_cgroup(p_data->pid, RESOURCED_BACKGROUND_APP_NAME); + } + + if (!nfacct_number) + _D("There wasn't any information about background application "\ + "in nfacct counters tree"); + return RESOURCED_ERROR_NONE; +} + + static gboolean populate_classid_tree(gpointer key, gpointer value, gpointer data) @@ -292,7 +508,8 @@ static int add_one_tizen_os_counter( { struct counter_arg *carg = (struct counter_arg *)data; struct nfacct_rule counter = {.name = {0}, .ifname = {0}, 0}; - resourced_iface_type iftype = *(resourced_iface_type *)value; + resourced_iface_type iftype = (resourced_iface_type)key; + char *ifname = (char *)value; if (iftype <= RESOURCED_IFACE_UNKNOWN || iftype >= RESOURCED_IFACE_LAST_ELEM) @@ -301,17 +518,22 @@ static int add_one_tizen_os_counter( counter.iotype = NFACCT_COUNTER_IN; counter.iftype = iftype; counter.carg = carg; + STRING_SAVE_COPY(counter.ifname, ifname); generate_counter_name(&counter); - add_iptables_in(&counter); + if (add_iptables_in(&counter) != RESOURCED_ERROR_NONE) + _D("Failed to add counter %s", counter.name); + counter.iotype = NFACCT_COUNTER_OUT; generate_counter_name(&counter); - add_iptables_out(&counter); + if (add_iptables_out(&counter) != RESOURCED_ERROR_NONE) + _D("Failed to add counter %s", counter.name); + return FALSE; } static void add_tizen_os_counters(struct counter_arg *carg) { - for_each_ifindex((ifindex_iterator)add_one_tizen_os_counter, NULL, carg); + for_each_ifnames((ifnames_iterator)add_one_tizen_os_counter, NULL, carg); } static void reload_all_nf_counters(struct counter_arg *carg) @@ -367,8 +589,9 @@ static inline char *get_public_appid(const uint32_t classid) /* following value for ALL is suitable for using in statistics what's why it's not in get_app_id_by_classid */ - if (classid == RESOURCED_ALL_APP_CLASSID) - return RESOURCED_ALL_APP; + if (classid == RESOURCED_ALL_APP_CLASSID || + classid == RESOURCED_BACKGROUND_APP_CLASSID) + return strdup(RESOURCED_ALL_APP); appid = get_app_id_by_classid(classid, true); return !appid ? UNKNOWN_APP : appid; @@ -384,6 +607,7 @@ static void init_nfacct(u_int32_t classid, pid_t pid, counter->pid = pid; counter->intend = NFACCT_COUNTER; counter->quota = 0; + counter->roaming = get_current_roaming(); if (ctype == NFACCT_COUNTER_IN) counter->iptables_rule = add_iptables_in; else if (ctype == NFACCT_COUNTER_OUT) @@ -397,6 +621,39 @@ static resourced_ret_c del_counter(struct nfacct_rule *counter) counter->iotype); } +static void fill_du_quota(const char *app_id, const char *ifname, + const resourced_iface_type iftype, data_usage_quota *du_quota, + int *quota_id, resourced_state_t ground) +{ + char *imsi_hash = get_imsi_hash(get_current_modem_imsi()); + resourced_roaming_type roaming = get_current_roaming(); + /* lookup in quota tree */ + resourced_ret_c ret = get_quota_by_appid(app_id, imsi_hash, iftype, + roaming, du_quota, quota_id, ground); + + du_quota->roaming_type = roaming; + _D("ret %d, quota id %d", ret, *quota_id); + /* if lookup wasn't successfull, searchin restriction db, + * for example we could faced with restriction without quota */ + if (ret != RESOURCED_ERROR_NONE || !*quota_id) { + resourced_restriction_info rst_info = {0}; + resourced_ret_c ret; + rst_info.ifname = ifname; + /* TODO add roaming into restriction info request */ + ret = get_restriction_info(app_id, iftype, &rst_info); + ret_msg_if(ret != RESOURCED_ERROR_NONE, + "Failed to get restriction info!"); + + get_quota_by_id(rst_info.quota_id, du_quota); + du_quota->roaming_type = rst_info.roaming; + *quota_id = rst_info.quota_id; + } + _D("quota rcv: % " PRId64 ", send: % " PRId64 " ", du_quota->rcv_quota, + du_quota->snd_quota); + _D("quota roaming: %d", du_quota->roaming_type); + +} + static int fill_restriction(struct rtattr *attr_list[__NFACCT_MAX], void *user_data) { @@ -404,18 +661,26 @@ static int fill_restriction(struct rtattr *attr_list[__NFACCT_MAX], struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, }; char *cnt_name = (char *)RTA_DATA( attr_list[NFACCT_NAME]); + + /* because foreground/background property wasn't + * in counter */ + resourced_state_t ground; char *app_id = 0; + int quota_id = 0; int ret = 0; - resourced_restriction_info rst_info = {0}; + data_usage_quota du_quota = {0}; init_nfacct(0, 0, 0, carg, &counter); strcpy(counter.name, cnt_name); recreate_counter_by_name(cnt_name, &counter); + ground = counter.classid == RESOURCED_BACKGROUND_APP_CLASSID ? + RESOURCED_STATE_BACKGROUND : RESOURCED_STATE_FOREGROUND; app_id = get_public_appid(counter.classid); - ret = get_restriction_info(app_id, counter.iftype, &rst_info); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "Failed to get restriction info!"); + ret_value_msg_if(!app_id, RESOURCED_ERROR_NONE, "Unknown app_id for %d", + counter.classid); + fill_du_quota(app_id, counter.ifname, counter.iftype, &du_quota, + "a_id, ground); if (counter.intend == NFACCT_BLOCK) { if (counter.iotype == NFACCT_COUNTER_IN) { @@ -434,17 +699,23 @@ static int fill_restriction(struct rtattr *attr_list[__NFACCT_MAX], * not yet fired rule */ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, "Can't create auxilary counter %s", out_counter.name); + ret = apply_tethering_restriction(RST_SET); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "Can't block tethering"); } - if (rst_info.quota_id != NONE_QUOTA_ID) - send_restriction_notification(app_id); + /* send restriction notification only in case of + * it was related to quota */ + if (quota_id != NONE_QUOTA_ID) + send_restriction_notification(app_id, &du_quota); + else + _D("No need to send restriction notification"); update_restriction_db(app_id, counter.iftype, 0, 0, RESOURCED_RESTRICTION_ACTIVATED, - rst_info.quota_id, rst_info.roaming); - + quota_id, du_quota.roaming_type, counter.ifname); } else if (counter.intend == NFACCT_WARN) { - if (rst_info.quota_id != NONE_QUOTA_ID) - send_restriction_warn_notification(app_id); + if (quota_id != NONE_QUOTA_ID) + send_restriction_warn_notification(app_id, &du_quota); /* remove both warnings */ counter.iotype = NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT; ret = del_counter(&counter); @@ -532,6 +803,16 @@ static void fini_notifier(struct counter_arg *carg) /* end notification section */ #else +static int app_launch_srv_cb(void *data) +{ + return 0; +} + +static int app_resume_cb(void *data) +{ + return 0; +} + static int app_terminate_cb(void *data) { return 0; @@ -558,19 +839,21 @@ static int resourced_datausage_init(void *data) "Invalid shared modules data\n"); /* register notifier cb */ register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb); - register_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb); - register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb); + register_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_resume_cb); + register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_srv_cb); register_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb); + register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, app_backgrnd_cb); m_data->carg = init_counter_arg(marg->opts); ret_code = resourced_iface_init(); ret_value_msg_if(ret_code < 0, ret_code, "resourced_iface_init failed"); - resourced_roaming_cb_init(); ret_code = resourced_init_counter_func(m_data->carg); ret_value_msg_if(ret_code < 0, ret_code, "Error init counter func\n"); resourced_add_vconf_datausage_cb(m_data->carg); - init_hw_net_protocol_type(); reactivate_restrictions(); + ret_code = resourced_init_db_guard(m_data->carg); + ret_value_msg_if(ret_code < 0, ret_code, "Error init db guard\n"); + #ifdef CONFIG_DATAUSAGE_NFACCT reload_all_nf_counters(m_data->carg); @@ -596,13 +879,14 @@ static int resourced_datausage_finalize(void *data) resourced_finalize_counter_func(m_data->carg); finalize_carg(m_data->carg); finalize_storage_stm(); - finalize_hw_net_protocol_type(); unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb); unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb); unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb); unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb); resourced_iface_finalize(); finalize_iftypes(); + finilize_telephony(); + ecore_timer_del(m_data->carg->erase_timer); return RESOURCED_ERROR_NONE; } @@ -624,12 +908,23 @@ static int compare_nfcntr(gconstpointer a, gconstpointer b, ret = key_a->iotype - key_b->iotype; if (ret) return ret; - return strcmp(key_a->ifname, key_b->ifname); + + ret = key_a->intend - key_b->intend; + if (ret) + return ret; + + /* in case of incomplete counters ->ifname will contain + * empty string, if we found incomplete counter, + * assume it's the same as given ifname */ + if (strlen(key_a->ifname) && strlen(key_b->ifname)) + return strcmp(key_a->ifname, key_b->ifname); + + return 0; } GTree *create_nfacct_tree(void) { - return g_tree_new_full(compare_nfcntr, NULL, NULL, free); + return g_tree_new_full(compare_nfcntr, NULL, free, free); } static struct nfacct_value *lookup_counter(struct nfacct_rule *counter) @@ -637,8 +932,10 @@ static struct nfacct_value *lookup_counter(struct nfacct_rule *counter) struct nfacct_key key = { .classid = counter->classid, .iftype = counter->iftype, - .iotype = counter->iotype + .iotype = counter->iotype, + .intend = counter->intend, }; + STRING_SAVE_COPY(key.ifname, counter->ifname); return (struct nfacct_value *)g_tree_lookup(counter->carg->nf_cntrs, @@ -649,73 +946,235 @@ static struct nfacct_value *lookup_counter(struct nfacct_rule *counter) void keep_counter(struct nfacct_rule *counter) { struct nfacct_key *key = NULL; - struct nfacct_value *value = NULL; + struct nfacct_value *value = lookup_counter(counter); - key = (struct nfacct_key *)malloc(sizeof( - struct nfacct_key)); - ret_msg_if(key == NULL, - "Can allocate memory for nfacct_key!"); + if (!value) { + key = (struct nfacct_key *)malloc(sizeof( + struct nfacct_key)); + ret_msg_if(key == NULL, + "Can allocate memory for nfacct_key!"); - value = (struct nfacct_value *)malloc(sizeof( - struct nfacct_value)); + value = (struct nfacct_value *)malloc(sizeof( + struct nfacct_value)); - if (value == NULL) { - free(key); - _D("Can allocate memory for nfacct_key!"); - return; + if (value == NULL) { + free(key); + _D("Can allocate memory for nfacct_key!"); + return; + } + key->classid = counter->classid; + key->iftype = counter->iftype; + key->iotype = counter->iotype; + key->intend = counter->intend; + STRING_SAVE_COPY(key->ifname, counter->ifname); + g_tree_insert(counter->carg->nf_cntrs, key, value); } - key->classid = counter->classid; - key->iftype = counter->iftype; - key->iotype = counter->iotype; - STRING_SAVE_COPY(key->ifname, counter->ifname); - value->pid = counter->pid; value->state = NFACCT_STATE_ACTIVE; + value->quota = counter->quota; + value->iptables_rule = counter->iptables_rule; + value->quota_id = counter->quota_id; + value->rst_state = counter->rst_state; + value->roaming = counter->roaming; + value->fini = 0; + value->ground = RESOURCED_STATE_FOREGROUND; + +#ifdef DEBUG_ENABLED + if (key && key->intend == NFACCT_BLOCK) + _D("%p rst_state: %d", value, value->rst_state); +#endif +} + +void finalize_counter(struct nfacct_rule *counter) +{ + resourced_ret_c ret; + struct nfacct_key nf_key = { + .classid = counter->classid, + .iftype = counter->iftype, + .iotype = counter->iotype, + .intend = counter->intend, + }; + struct nfacct_value *value; + + STRING_SAVE_COPY(nf_key.ifname, counter->ifname); + value = (struct nfacct_value *)g_tree_lookup(counter->carg->nf_cntrs, + &nf_key); + +#ifdef DEBUG_ENABLED + _D("counter name: %s", counter->name); + _D("counter classid: %d", counter->classid); + _D("counter iftype: %d", counter->iftype); + _D("counter iotype: %d", counter->iotype); + _D("counter ifname: %s", counter->ifname); +#endif /* DEBUG_ENABLED */ + + ret_msg_if(!value, "Can't find counter", counter->name); + if (!CHECK_BIT(value->fini, NFACCT_FINAL_REMOVE)) { +#ifdef DEBUG_ENABLED + _D("No need to remove value %p", value); +#endif + return; + } +#ifdef DEBUG_ENABLED + else { + _D("remove value %p", value); + } +#endif + ret_msg_if(!value->iptables_rule, "There is no iptables_rule handler"); + + ret = value->iptables_rule(counter); + ret_msg_if (ret != RESOURCED_ERROR_NONE, "Failed to execute iptables rule"); + UNSET_BIT(value->fini, NFACCT_FINAL_REMOVE); + value->state = NFACCT_STATE_DEACTIVATED; +#ifdef DEBUG_ENABLED + if (nf_key.intend == NFACCT_BLOCK) + _D("%p rst_state: %d", value, value->rst_state); +#endif +} + +void set_finalize_flag(struct nfacct_rule *counter) +{ + struct nfacct_key nf_key = { + .classid = counter->classid, + .iftype = counter->iftype, + .iotype = counter->iotype, + .intend = counter->intend, + }; + struct nfacct_value *value; + + STRING_SAVE_COPY(nf_key.ifname, counter->ifname); + value = lookup_counter(counter); + ret_msg_if(!value, "Can't find counter for set finalize state!"); + SET_BIT(value->fini, NFACCT_FINAL_REMOVE); + value->iptables_rule = nfacct_send_del; + value->rst_state = counter->rst_state; +#ifdef DEBUG_ENABLED + if (nf_key.intend == NFACCT_BLOCK) + _D("%p rst_state: %d", value, value->rst_state); +#endif + if (counter->carg && counter->carg->opts) + SET_BIT(counter->carg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE); +} + +static gboolean fill_restriction_list(gpointer key, gpointer value, + gpointer data) +{ + GSList **rst_list = (GSList **)data; + GSList *iter = NULL; + resourced_restriction_info *info = NULL; + + struct nfacct_key *nf_key = (struct nfacct_key *)key; + struct nfacct_value *nf_value = (struct nfacct_value *)value; + char *app_id; + + /* only restriction guard is needed here */ + if (nf_key->intend != NFACCT_BLOCK) + return FALSE; + + app_id = get_public_appid(nf_key->classid); + ret_value_msg_if(!app_id, FALSE, "Can't get appid"); + + gslist_for_each_item(iter, *rst_list) { + resourced_restriction_info *look_info = (resourced_restriction_info *)iter->data; + if (look_info->app_id && !strcmp(look_info->app_id, app_id) && + look_info->iftype == nf_key->iftype && + look_info->quota_id == nf_value->quota_id && + look_info->roaming == nf_value->roaming) { + info = look_info; + break; + } + } + + if (!info) { + _D("We didn't find this restriction in list! Create new one!"); + info = (resourced_restriction_info *)malloc(sizeof(resourced_restriction_info)); + ret_value_msg_if (!info, FALSE, "Can't allocate memory"); + memset(info, 0, sizeof(resourced_restriction_info)); + *rst_list = g_slist_prepend(*rst_list, info); + } + +#ifdef DEBUG_ENABLED + if (nf_key->intend == NFACCT_BLOCK) + _D("%p rst_state: %d", nf_value, nf_value->rst_state); +#endif - g_tree_insert(counter->carg->nf_cntrs, key, value); + if (info->iftype == RESOURCED_IFACE_UNKNOWN) + info->iftype = nf_key->iftype; + if (info->quota_id == NONE_QUOTA_ID) + info->quota_id = nf_value->quota_id; + if (info->roaming == RESOURCED_ROAMING_UNKNOWN) + info->roaming = nf_value->roaming; + if (!info->ifname) + info->ifname = strdup(nf_key->ifname); + if (!info->app_id) + info->app_id = app_id; + if (!info->rst_state) + info->rst_state = nf_value->rst_state == RESOURCED_RESTRICTION_REMOVED ? RESOURCED_RESTRICTION_ACTIVATED : nf_value->rst_state; + + if (nf_key->iotype == NFACCT_COUNTER_IN) + info->rcv_limit = nf_value->quota; + else if(nf_key->iotype == NFACCT_COUNTER_OUT) + info->send_limit = nf_value->quota; + else + _D("Unknown iotype"); + + return FALSE; +} + +void extract_restriction_list(struct counter_arg *arg, + GSList **rst_list) +{ + /* to avoid duplication and search while filling rst_list */ + g_tree_foreach(arg->nf_cntrs, fill_restriction_list, rst_list); +} + +void update_counter_quota_value(struct nfacct_rule *counter, uint64_t bytes) +{ + struct nfacct_value *value = lookup_counter(counter); + ret_msg_if(!value, "Can't find nfacct entry for %s", counter->name); + if (value->quota <= bytes) { + _D("overquoted % " PRIu64 " ", bytes); + value->quota = 0; + } else + value->quota -= bytes; } static int create_each_iptable_rule(gpointer key, gpointer value, void *data) { struct make_rule_context *ctx = (struct make_rule_context *)data; resourced_ret_c ret; - resourced_iface_type iftype = *(resourced_iface_type *)value; + resourced_iface_type iftype = (resourced_iface_type)key; + char *ifname = (char *)value; struct nfacct_value *counter = NULL; if (iftype <= RESOURCED_IFACE_UNKNOWN || - iftype >= RESOURCED_IFACE_LAST_ELEM) { + iftype >= RESOURCED_IFACE_LAST_ELEM || + iftype == RESOURCED_IFACE_BLUETOOTH) { _D("Unsupported network interface type %d", iftype); return RESOURCED_ERROR_NONE; } ctx->counter->iftype = iftype; + STRING_SAVE_COPY(ctx->counter->ifname, ifname); generate_counter_name(ctx->counter); counter = lookup_counter(ctx->counter); - if (counter != NULL) { - _D("Counter already exists!"); - return RESOURCED_ERROR_NONE; + if (!counter || + (counter->state != NFACCT_STATE_ACTIVE)) { + ret = ctx->counter->iptables_rule(ctx->counter); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL, + "Can't add iptables ingress rule"); } - ret = ctx->counter->iptables_rule(ctx->counter); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL, - "Can't add iptables ingress rule"); - keep_counter(ctx->counter); return RESOURCED_ERROR_NONE; } static void populate_incomplete_counter(void *data) { struct make_rule_context *ctx = (struct make_rule_context *)data; - struct nfacct_value *counter; generate_counter_name(ctx->counter); - counter = lookup_counter(ctx->counter); - if (counter != NULL) { - _D("Counter already exists!"); - return; - } keep_counter(ctx->counter); } @@ -731,12 +1190,12 @@ static resourced_ret_c create_iptables_rule(const char *app_id, const pid_t pid) ctx.counter = &counter; init_nfacct(classid, pid, NFACCT_COUNTER_IN, carg, &counter); - for_each_ifindex((ifindex_iterator)create_each_iptable_rule, + for_each_ifnames((ifnames_iterator)create_each_iptable_rule, populate_incomplete_counter, &ctx); counter.iotype = NFACCT_COUNTER_OUT; counter.iptables_rule = add_iptables_out; - for_each_ifindex((ifindex_iterator)create_each_iptable_rule, + for_each_ifnames((ifnames_iterator)create_each_iptable_rule, populate_incomplete_counter, &ctx); return RESOURCED_ERROR_NONE; @@ -755,6 +1214,27 @@ static bool is_incomplete_counter(struct nfacct_key *nfacct_key, struct nfacct_v /* special incomplete status unnecessary */ } +static void trace_nf_key_value(struct nfacct_key *nfacct_key, struct nfacct_value *nfacct_value) +{ +#ifdef DEBUG_ENABLED + _D("-------- NF TREE NODE -----------"); + _D("classid: %d", nfacct_key->classid); + _D("iftype: %d", nfacct_key->iftype); + _D("iotype: %d", nfacct_key->iotype); + _D("ifname: %s", nfacct_key->ifname); + _D("pid: %d", nfacct_value->pid); + _D("state: %d", nfacct_value->state); + _D("fini: %d", nfacct_value->fini); + _D("intend: %d", nfacct_key->intend); + _D("quota: %" PRIu64 " ", nfacct_value->quota); + _D("quota_id:%d", nfacct_value->quota_id); + _D("roaming: %d", nfacct_value->roaming); + if (nfacct_key->intend == NFACCT_BLOCK) + _D("%p rst_state:%d", nfacct_value, nfacct_value->rst_state); + _D("---------NF TREE NODE -----------"); +#endif /* DEBUG_ENABLED */ +} + static gboolean activate_each_counter_by_iftype(gpointer key, gpointer value, gpointer data) @@ -766,6 +1246,18 @@ static gboolean activate_each_counter_by_iftype(gpointer key, struct nfacct_value *found_counter; int ret = RESOURCED_ERROR_NONE; + + trace_nf_key_value(nfacct_key, nfacct_value); + /* skip restriction and warning here due there is special + * logic for it in restriction-handler.c, + * maybe it worth to merge that logic and remove + * handler in restriction-handler, but need to take + * imsi into account */ + if (nfacct_key->intend == NFACCT_BLOCK || + nfacct_key->intend == NFACCT_WARN) { + _D("skip block and warning"); + return FALSE; + } /* ugly check, due in case of RMNET -> WLAN switch, * WLAN activated before then RMNET is deactivated */ @@ -787,6 +1279,15 @@ static gboolean activate_each_counter_by_iftype(gpointer key, * WLAN is preffered, so lets deactivate it */ return FALSE; /* continue iteration */ + /* counter still active it wasn't yet removed, + * del_counter_delayed isn't yet called */ + if (nfacct_value->state == NFACCT_STATE_DEL_DELAYED && + ctx->iftype == nfacct_key->iftype) { + /* in del_counter_delayed we'll free context + * and skip removing */ + nfacct_value->state = NFACCT_STATE_ACTIVE; + return FALSE; + } counter.classid = nfacct_key->classid; counter.iotype = nfacct_key->iotype; @@ -815,8 +1316,6 @@ static gboolean activate_each_counter_by_iftype(gpointer key, if (found_counter != NULL && found_counter->state == NFACCT_STATE_DEACTIVATED) found_counter->state = NFACCT_STATE_ACTIVE; - else - keep_counter(&counter); return FALSE; } @@ -838,7 +1337,7 @@ static void handle_on_iface_up(const int ifindex) ctx.iftype = iftype; ctx.carg = m_data->carg; g_tree_foreach(ctx.carg->nf_cntrs, activate_each_counter_by_iftype, &ctx); - add_tizen_os_counters(m_data->carg); + add_one_tizen_os_counter((gpointer)iftype, NULL, m_data->carg); } struct del_counter_context @@ -855,6 +1354,10 @@ static Eina_Bool del_counter_delayed(void *data) struct del_counter_context *del_ctx = (struct del_counter_context *)data; struct nfacct_value *nfacct_value = del_ctx->nfacct_value; struct nfacct_key *nfacct_key = del_ctx->nfacct_key; + if (nfacct_value->state != NFACCT_STATE_DEL_DELAYED) { + _D("nfacct counter state is %d", nfacct_value->state); + goto out; + } counter.classid = nfacct_key->classid; counter.iotype = nfacct_key->iotype; @@ -866,12 +1369,16 @@ static Eina_Bool del_counter_delayed(void *data) ret = del_counter(&counter); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ECORE_CALLBACK_CANCEL, - "Can't delete counter %s", - counter.name); + if(ret != RESOURCED_ERROR_NONE) { + _E("Can't delete counter %s", + counter.name); + goto out; + } nfacct_value->state = NFACCT_STATE_DEACTIVATED; +out: + free(del_ctx); return ECORE_CALLBACK_CANCEL; } @@ -884,8 +1391,12 @@ static gboolean deactivate_each_counter_by_iftype(gpointer key, struct iftype_context *ctx = (struct iftype_context *)data; struct del_counter_context *del_ctx = NULL; - /* deactivate counters only for ctx->iftype interface */ - if (ctx->iftype != nfacct_key->iftype) + /* deactivate counters only for ctx->iftype interface, + * and only counters warning/restriction will be removed in + * another _reset_restriction_iter */ + if (ctx->iftype != nfacct_key->iftype || + nfacct_key->intend == NFACCT_WARN || + nfacct_key->intend == NFACCT_BLOCK) return FALSE; /* continue iteration */ del_ctx = (struct del_counter_context *)malloc( @@ -895,6 +1406,7 @@ static gboolean deactivate_each_counter_by_iftype(gpointer key, del_ctx->nfacct_key = nfacct_key; del_ctx->nfacct_value = nfacct_value; del_ctx->carg = ctx->carg; + nfacct_value->state = NFACCT_STATE_DEL_DELAYED; ecore_timer_add(0, del_counter_delayed, del_ctx); return FALSE; @@ -941,9 +1453,9 @@ iface_callback *create_counter_callback(void) resourced_ret_c join_net_cls(const char *app_id, const pid_t pid) { resourced_ret_c ret; - char pkgname[MAX_PATH_LENGTH]; - extract_pkgname(app_id, pkgname, sizeof(pkgname)); - ret = make_net_cls_cgroup_with_pid(pid, pkgname); + ret_value_msg_if(!app_id, RESOURCED_ERROR_INVALID_PARAMETER, + "invalid app_id"); + ret = make_net_cls_cgroup_with_pid(pid, app_id); ret_value_if(ret != RESOURCED_ERROR_NONE, ret); ret = update_classids(); ret_value_if(ret != RESOURCED_ERROR_NONE, ret); diff --git a/src/network/datausage-quota-processing.c b/src/network/datausage-quota-processing.c index 11822b49..069d85a4 100644 --- a/src/network/datausage-quota-processing.c +++ b/src/network/datausage-quota-processing.c @@ -34,17 +34,20 @@ #include <sqlite3.h> #include <inttypes.h> +#include "counter.h" #include "database.h" #include "data_usage.h" #include "macro.h" -#include "protocol-info.h" #include "resourced.h" +#include "net-cls-cgroup.h" #include "notification.h" #include "storage.h" #include "trace.h" -#include "roaming.h" +#include "telephony.h" +#include "datausage-common.h" #include "datausage-restriction.h" #include "datausage-vconf-common.h" +#include "datausage-quota-processing.h" static GTree *quotas; static sqlite3_stmt *select_stmt; @@ -58,20 +61,23 @@ static const char select_query[] = "SELECT qt.binpath, qt.sent_quota, qt.rcv_quo "efq.finish_time AS effective_finish, qt.iftype AS iftype, " \ "qt.roaming, "\ "efq.state, "\ - "qt.ROWID "\ + "qt.ROWID, "\ + "qt.imsi, "\ + "qt.ground "\ "FROM quotas AS qt "\ "LEFT OUTER JOIN effective_quotas AS efq ON (qt.binpath = efq.binpath "\ - "AND qt.iftype = efq.iftype AND qt.roaming = efq.roaming) "\ + "AND qt.iftype = efq.iftype AND qt.roaming = efq.roaming "\ + "AND qt.imsi = efq.imsi) "\ "GROUP BY qt.binpath, qt.iftype, qt.sent_quota, qt.rcv_quota, " \ - "qt.roaming"; + "qt.roaming, qt.imsi"; static const char insert_query[] = "REPLACE INTO effective_quotas " \ "(binpath, sent_used_quota, rcv_used_quota, " \ - "start_time, finish_time, iftype, roaming, state) " \ - " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + "start_time, finish_time, iftype, roaming, state, imsi) " \ + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; static const char clear_effective_quota_query[] = "DELETE FROM effective_quotas " \ - " WHERE binpath = ? AND iftype = ? AND roaming = ?"; + " WHERE binpath = ? AND iftype = ? AND roaming = ? AND imsi = ?"; enum resourced_quota_state { RESOURCED_QUOTA_UNKNOWN, /**< undefined/initial state */ @@ -98,6 +104,8 @@ struct quota_key { const char *app_id; resourced_iface_type iftype; resourced_roaming_type roaming; + const char *imsi_hash; + resourced_state_t ground; }; typedef enum { @@ -106,33 +114,35 @@ typedef enum { DROP_NO_NEED = 2 } drop_decision; -static void obtain_and_keep_quotas(sqlite3_stmt *query) +static inline bool check_imsi_hash(const char *hash_a, const char *hash_b); + +static resourced_ret_c obtain_and_keep_quotas(sqlite3_stmt *query) { int rc = 0; + resourced_ret_c ret = RESOURCED_ERROR_NONE; struct quota *value = 0; struct quota_key *key = 0; - if (!query) { - _D("Can not update quotas: empty query"); - return; - } + ret_value_msg_if(!query, RESOURCED_ERROR_INVALID_PARAMETER, + "Can not update quotas: empty query"); do { rc = sqlite3_step(query); if (rc == SQLITE_ERROR) { _E("Error updating quotas %s", sqlite3_errmsg(resourced_get_database())); - return; + return RESOURCED_ERROR_DB_FAILED; } else if (rc == SQLITE_ROW) { value = g_new0(struct quota, 1); if (!value) { _E("Can't allocate value for quota"); - return; + return RESOURCED_ERROR_OUT_OF_MEMORY; } key = g_new0(struct quota_key, 1); if (!key) { _E("Can't allocate key for quota"); + ret = RESOURCED_ERROR_OUT_OF_MEMORY; goto free_value; } @@ -142,6 +152,9 @@ static void obtain_and_keep_quotas(sqlite3_stmt *query) query, 11); key->roaming = sqlite3_column_int( query, 12); + key->imsi_hash = strdup((char *)sqlite3_column_text( + query, 15)); + key->ground = sqlite3_column_int(query, 16); value->send_quota = sqlite3_column_int64( query, 1); @@ -168,14 +181,21 @@ static void obtain_and_keep_quotas(sqlite3_stmt *query) value->quota_id = sqlite3_column_int( query, 14); + _D("populate quota tree:"); + _D("app_id: %s", key->app_id); + _D("iftype: %d", key->iftype); + _D("roaming: %d", key->roaming); + _D("imsi_hash: %s", key->imsi_hash); + _D("quota_id: %d", value->quota_id); + _D("ground: %d", key->ground); g_tree_insert(quotas, key, value); } } while (rc == SQLITE_ROW); - return; + return RESOURCED_ERROR_NONE; free_value: - if (value) - g_free(value); + g_free(value); + return ret; } static gint compare_quota_key(gconstpointer a, gconstpointer b, @@ -183,18 +203,56 @@ static gint compare_quota_key(gconstpointer a, gconstpointer b, { const struct quota_key *key1 = a; const struct quota_key *key2 = b; - /* the first part of the key is equal compare second */ - return strcmp(key1->app_id, key2->app_id) || - key1->iftype - key2->iftype || - key1->roaming - key2->roaming; + int ret = 0; + + /* the main use case of setting it's different quotas + * per sim, and only afterward by appid */ + + if (key1->imsi_hash && key2->imsi_hash) + ret = strcmp(key1->imsi_hash, key2->imsi_hash); + else if (!key1->imsi_hash || !key2->imsi_hash) /* in case of one empty another not */ + ret = key1->imsi_hash - key2->imsi_hash; + + if (ret) { + _D("quotas different by imsi"); + return ret; + } + + if (key1->app_id && key2->app_id) + ret = strcmp(key1->app_id, key2->app_id); + if (ret) { + _D("quotas different by app_id"); + return ret; + } + + ret = key1->iftype - key2->iftype; + if (ret) { + _D("quotas different by iftype"); + return ret; + } + ret = key1->ground - key2->ground; + if (ret) { + _D("quotas different by ground"); + return ret; + } + return key1->roaming - key2->roaming; } -#define quota_key_destructor g_free +static void quota_key_destructor(void *key) +{ + struct quota_key *qkey = (struct quota_key *)key; + if (qkey->app_id) + free((char *)qkey->app_id); + if (qkey->imsi_hash) + free((char *)qkey->imsi_hash); + g_free(key); +} #define quota_destructor g_free -static void _clear_effective_quota(const char *app_id, +void clear_effective_quota(const char *app_id, const resourced_iface_type iftype, - const resourced_roaming_type roaming) + const resourced_roaming_type roaming, + const char *imsi_hash) { if (sqlite3_bind_text(clear_effective_stmt, 1, app_id, -1, SQLITE_TRANSIENT) != SQLITE_OK) { @@ -217,6 +275,13 @@ static void _clear_effective_quota(const char *app_id, return; } + if (sqlite3_bind_text(clear_effective_stmt, 4, imsi_hash, -1, SQLITE_TRANSIENT) + != SQLITE_OK) { + _E("Can not bind subscriber_id:%s for preparing statement:%s", + imsi_hash, sqlite3_errmsg(resourced_get_database())); + return; + } + if (sqlite3_step(clear_effective_stmt) != SQLITE_DONE) _E("Failed to clear effective quotas %s", sqlite3_errmsg(resourced_get_database())); @@ -269,7 +334,7 @@ static data_usage_quota_period_t _define_period(const int time_period, int *quan } -static time_t _get_finish_time(const time_t start_time, const int time_period) +static time_t get_finish_time(const time_t start_time, const int time_period) { int quantity = 0; struct tm *new_start = gmtime((const time_t *)&start_time); @@ -299,6 +364,8 @@ struct data_usage_context { int64_t sent_used_quota; int64_t rcv_used_quota; resourced_roaming_type roaming; + const char *imsi; + resourced_state_t ground; }; static resourced_cb_ret data_usage_details_cb(const data_usage_info *info, @@ -307,14 +374,25 @@ static resourced_cb_ret data_usage_details_cb(const data_usage_info *info, struct data_usage_context *context = (struct data_usage_context *)user_data; - if (!context || - (context->roaming != RESOURCED_ROAMING_UNKNOWN && - context->roaming != info->roaming)) + ret_value_msg_if(!context, RESOURCED_CONTINUE, + "Invalid cb data!"); + + if (context->roaming != info->roaming) + return RESOURCED_CONTINUE; + + if (!CHECK_BIT(context->ground, info->ground)) return RESOURCED_CONTINUE; - context->sent_used_quota = info->foreground.cnt.incoming_bytes; - context->rcv_used_quota = info->foreground.cnt.outgoing_bytes; - return RESOURCED_CANCEL; /* only one entry allowed */ + /* if imsi is not specified, e.g. for WiFi + * need additional check*/ + if (info->imsi && context->imsi && strcmp(context->imsi, info->imsi)) + return RESOURCED_CONTINUE; + + context->sent_used_quota += info->cnt.outgoing_bytes; + context->rcv_used_quota += info->cnt.incoming_bytes; + /* calculate all traffic, several iteration could be + * needed when end user request quota for unknown roaming */ + return RESOURCED_CONTINUE; } static void _record_quota(const struct quota_key *key, @@ -387,25 +465,40 @@ static void _record_quota(const struct quota_key *key, return; } + if (sqlite3_bind_text(insert_stmt, 9, key->imsi_hash, -1, + SQLITE_STATIC) + != SQLITE_OK) { + _E("Can not bind subscriber_id:%s for preparing statement", + key->imsi_hash); + return; + } + if (sqlite3_step(insert_stmt) != SQLITE_DONE) _D("Failed to record quotas %s", sqlite3_errmsg(resourced_get_database())); sqlite3_reset(insert_stmt); } -static void _set_effective_quota(const char *app_id, +static time_t rule_start_time(time_t start_time, + time_t cur_time, + time_t time_interval) +{ + if (cur_time - start_time > time_interval) + return cur_time - (cur_time - start_time) % time_interval; + return start_time; +} + +static void set_effective_quota(const char *app_id, const resourced_iface_type iftype, const time_t start_time, const int time_period, - const resourced_roaming_type roaming) + const resourced_roaming_type roaming, + const char *imsi_hash, + const resourced_state_t ground, + struct quota *app_quota) { data_usage_selection_rule rule = {0,}; struct data_usage_context out_context = {0,}; - struct quota_key key_quota = { - .app_id = app_id, - .iftype = iftype, - .roaming = roaming, - }; - struct quota app_quota = {0,}; const time_t cur_time = time(0); + app_id = !strcmp(app_id, RESOURCED_ALL_APP) ? 0: app_id; if (cur_time < start_time) { _D("No need to update effective quota!"); @@ -413,7 +506,13 @@ static void _set_effective_quota(const char *app_id, } out_context.roaming = roaming; - rule.from = start_time; + out_context.imsi = imsi_hash; + out_context.ground = ground; + /* user could specify start_time far ago in the past, and + * we will recalculate since that time, it's not good, + * especially if time_period is smaller then + * current_time - start_time */ + rule.from = rule_start_time(start_time, cur_time, time_period); rule.to = cur_time; rule.iftype = iftype; @@ -423,58 +522,161 @@ static void _set_effective_quota(const char *app_id, return; } - _SD("Get counted traffic for appid:%s, per" - "%s, incoming:%d, outgoing:%d", app_id, ctime(&start_time), - out_context.rcv_used_quota, out_context.sent_used_quota); - - app_quota.sent_used_quota = out_context.sent_used_quota; - app_quota.rcv_used_quota = out_context.rcv_used_quota; - app_quota.real_start = start_time; - app_quota.real_finish = _get_finish_time(start_time, time_period); - app_quota.state = RESOURCED_QUOTA_APPLIED; - _record_quota(&key_quota, &app_quota); + _SD("Get counted traffic for appid:%s, per %s "\ + "time interval %d, incoming:%" PRId64 ", outgoing:%" PRId64 "", app_id, + ctime(&rule.from), time_period, out_context.rcv_used_quota, + out_context.sent_used_quota); + + app_quota->sent_used_quota = out_context.sent_used_quota; + app_quota->rcv_used_quota = out_context.rcv_used_quota; + app_quota->real_start = rule.from; /* otherwise we could get + real_finish in the past */ + app_quota->real_finish = get_finish_time(app_quota->real_start, + time_period); } -void update_quota_state(const char *app_id, - const resourced_iface_type iftype, - const time_t start_time, - const int time_period, - const resourced_roaming_type roaming) +static struct quota *find_quota_in_tree(const char *app_id, + const resourced_iface_type iftype, const resourced_roaming_type roaming, + const char *imsi, const resourced_state_t ground) { struct quota_key key; + key.app_id = app_id; + key.iftype = iftype; + key.roaming = roaming; + key.imsi_hash = imsi; + key.ground = ground; + return (struct quota *)g_tree_lookup(quotas, &key); +} + +bool check_quota_applied(const char *app_id, const resourced_iface_type iftype, + const resourced_roaming_type roaming, const char *imsi, + const resourced_state_t ground, int *quota_id) +{ + struct quota *tree_value = find_quota_in_tree(app_id, iftype, roaming, + imsi, ground); + + if (!tree_value) + return false; + *quota_id = tree_value->quota_id; + return tree_value->state == RESOURCED_QUOTA_APPLIED; +} + +void update_quota_state(const char *app_id, const int quota_id, + struct serialization_quota *ser_quota) +{ struct quota *tree_value; + struct quota_key *insert_key; if (!app_id) { _SE("app_id must be not NULL"); return; } - key.app_id = app_id; - key.iftype = iftype; - key.roaming = roaming; - tree_value = (struct quota *)g_tree_search(quotas, - (GCompareFunc)compare_quota_key, &key); + tree_value = find_quota_in_tree(app_id, ser_quota->iftype, + ser_quota->roaming_type, + ser_quota->imsi_hash, ser_quota->quota_type); + if (!check_event_in_current_modem(ser_quota->imsi_hash, + ser_quota->iftype)) + check_and_clear_all_noti(); if (tree_value && tree_value->state == RESOURCED_QUOTA_APPLIED) { - _SD("Removing quota and restriction for %s,%d", app_id, iftype); + _SD("Removing quota and restriction for %s,%d, %s", app_id, + ser_quota->iftype, ser_quota->imsi_hash); /* Restrictions can't be separated */ - remove_restriction_local(app_id, iftype); - g_tree_remove(quotas, (gconstpointer*)(&key)); - _clear_effective_quota(app_id, iftype, roaming); + if (remove_restriction_local(app_id, ser_quota->iftype, + tree_value->quota_id, ser_quota->imsi_hash, + ser_quota->quota_type) == RESOURCED_ERROR_NONE) + tree_value->state = RESOURCED_QUOTA_REVERTED; + else + _D("failed to revert quota %d", tree_value->quota_id); + + clear_effective_quota(app_id, ser_quota->iftype, + ser_quota->roaming_type, ser_quota->imsi_hash); + } else if (!tree_value) { + insert_key = malloc(sizeof(struct quota_key)); + ret_msg_if (!insert_key, "not enough memory"); + memset(insert_key, 0, sizeof(struct quota_key)); + tree_value = (struct quota *)malloc(sizeof(struct quota)); + if (!tree_value) { + _E("not enough memory"); + goto release_quota_key; + } + + memset(tree_value, 0, sizeof(struct quota)); + /* app_id was allocated by dbus, and it will be freed + * when dbus request is gone */ + insert_key->app_id = strdup(app_id); + if (!insert_key->app_id) { + _E("not enough memory"); + goto release_quota_value; + } - if (start_time && time_period) - _set_effective_quota(app_id, iftype, start_time, - time_period, roaming); - } else - _SD("There is no quota %s,%d in tree", app_id, iftype); + insert_key->imsi_hash = strdup(ser_quota->imsi_hash); + if (!insert_key->imsi_hash) { + _E("not enough memory"); + goto release_app_id; + } + insert_key->iftype = ser_quota->iftype; + insert_key->roaming = ser_quota->roaming_type; + _SD("There is no quota %s,%d in tree", app_id, + ser_quota->iftype); + insert_key->ground = ser_quota->quota_type; + g_tree_insert(quotas, insert_key, tree_value); + } + + /* we already stored quota, so _set_effective_quota, stores + * effective quota in db with new calculated value for exceeded + * trafifc */ + tree_value->send_quota = ser_quota->snd_quota; + tree_value->rcv_quota = ser_quota->rcv_quota; + /* + * in case of APPLIED/REVERTED quota used traffic need to clear + * it will be recalculated in set_effective_quota, due start + * time could be changed, + * also data_usage_details_foreach could fail, or user + * could not specify start_time and time_period + */ + tree_value->sent_used_quota = 0; + tree_value->rcv_used_quota = 0; + tree_value->snd_warning_threshold = ser_quota->snd_warning_threshold; + tree_value->rcv_warning_threshold = ser_quota->rcv_warning_threshold; + /* link with restriction */ + tree_value->quota_id = quota_id; + set_effective_quota(app_id, ser_quota->iftype, ser_quota->start_time, + ser_quota->time_period, ser_quota->roaming_type, + ser_quota->imsi_hash, ser_quota->quota_type, + tree_value); + + return; + +release_app_id: + free((char *)insert_key->app_id); +release_quota_value: + free(tree_value); +release_quota_key: + free(insert_key); +} + +void remove_quota_from_counting(const char *app_id, const resourced_iface_type iftype, + const resourced_roaming_type roaming, + const char *imsi_hash) +{ + struct quota_key key; + ret_msg_if(!app_id,"app_id must be not NULL"); + + key.app_id = app_id; + key.iftype = iftype; + key.roaming = roaming; + key.imsi_hash = strdup(imsi_hash); + + g_tree_remove(quotas, (gconstpointer*)(&key)); } + static resourced_ret_c _init_quotas(void) { - execute_once { - quotas = g_tree_new_full(compare_quota_key, NULL, + quotas = g_tree_new_full(compare_quota_key, NULL, quota_key_destructor, quota_destructor); - } if (!resourced_get_database()) return RESOURCED_ERROR_DB_FAILED; @@ -521,34 +723,29 @@ handle_error: /** * Update quotas tree, where app_id will the key */ -static resourced_ret_c _update_quotas(void) +static resourced_ret_c load_quotas(void) { const resourced_ret_c ret = _init_quotas(); - if (ret != RESOURCED_ERROR_NONE) { - _E("Failed to init quotas"); - return ret; - } - - obtain_and_keep_quotas(select_stmt); - return RESOURCED_ERROR_NONE; + ret_value_msg_if (ret != RESOURCED_ERROR_NONE, ret, "Failed to init quotas"); + return obtain_and_keep_quotas(select_stmt); } static const int64_t quota_gap_value[RESOURCED_IFACE_ALL] = { - 5000, /* ~4.5MB UNKNOWN */ - 5000, /* ~3MB RESOURCED_IFACE_DATACALL */ + 400000, /* ~4.5MB UNKNOWN */ + 400000, /* ~3MB RESOURCED_IFACE_DATACALL */ 6000000, /* ~6MB RESOURCED_IFACE_WIFI */ 5000000, /* ~100MB RESOURCED_IFACE_WIRED */ 6000000, /* ~6MB RESOURCED_IFACE_BLUETOOTH */ }; static const int64_t quota_datacall_gap_value[RESOURCED_PROTOCOL_MAX_ELEM] = { - 5000, /* RESOURCED_PROTOCOL_NONE */ - 5000, /* RESOURCED_PROTOCOL_DATACALL_NOSVC */ - 5000, /* RESOURCED_PROTOCOL_DATACALL_EMERGENCY */ - 5000, /* RESOURCED_PROTOCOL_DATACALL_SEARCH */ - 5000, /* RESOURCED_PROTOCOL_DATACALL_2G */ - 5000, /* RESOURCED_PROTOCOL_DATACALL_2_5G #GPRS 40 kbit/s in practice */ - 18750, /* RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE 150 kbit/s in practice */ + 400000, /* RESOURCED_PROTOCOL_NONE */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_NOSVC */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_EMERGENCY */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_SEARCH */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_2G */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_2_5G #GPRS 40 kbit/s in practice */ + 400000, /* RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE 150 kbit/s in practice */ 400000, /* RESOURCED_PROTOCOL_DATACALL_3G, 7Mb/s on QC device */ 475000, /* RESOURCED_PROTOCOL_DATACALL_HSDPA */ 5000000,/* RESOURCED_PROTOCOL_DATACALL_LTE */ @@ -560,7 +757,8 @@ static const int64_t quota_datacall_gap_value[RESOURCED_PROTOCOL_MAX_ELEM] = { static int64_t _get_quota_gap(const resourced_iface_type iftype) { - const resourced_hw_net_protocol_type proto = get_hw_net_protocol_type(iftype); + const resourced_hw_net_protocol_type proto = get_current_protocol(iftype); + _D("proto: %d, iftype: %d", proto, iftype); if (proto != RESOURCED_PROTOCOL_NONE) return quota_datacall_gap_value[proto]; @@ -572,7 +770,7 @@ static int64_t _get_quota_gap(const resourced_iface_type iftype) return quota_gap_value[RESOURCED_IFACE_UNKNOWN]; } -int _is_under_restriction(const int64_t send_delta, +static int check_restriction_needed(const int64_t send_delta, const int64_t rcv_delta, const resourced_iface_type iftype, int update_period) @@ -587,21 +785,11 @@ int _is_under_restriction(const int64_t send_delta, rcv_delta <= quota_gap; } -inline void _check_warning_threshold(const int64_t send_delta, const int64_t rcv_delta, - struct quota *app_quota, const char *appid) +inline static int get_warning_limit(int64_t delta, int64_t limit, int threshold) { - ret_msg_if(!app_quota, "Please provide valid pointer"); + if (delta < threshold) + return 0; /* send warning immediately */ - if (send_delta <= app_quota->snd_warning_threshold || - rcv_delta <= app_quota->rcv_warning_threshold) { - app_quota->snd_warning_threshold = 0; - app_quota->rcv_warning_threshold = 0; - send_restriction_warn_notification(appid); - } -} - -inline static int _get_warning_limit(int64_t limit, int threshold) -{ if (limit < threshold) { _E("Warning threshold is greater than limit!"); return WARNING_THRESHOLD_DEFAULT; /* 0 means kernel will @@ -619,72 +807,146 @@ static int cast_restriction_limit(int64_t delta) return delta; } +static bool skip_quota(struct quota_key *key_quota, struct quota *app_quota, + const int64_t send_delta, const int64_t rcv_delta) +{ + char *imsi_hash; + /* do not check already applied quota*/ + if (app_quota->state == RESOURCED_QUOTA_APPLIED) { + _D("already applied"); + return true; + } + + if (!strcmp(key_quota->app_id, TETHERING_APP_NAME) && + (send_delta > 0 || rcv_delta > 0)) { + _D("tethering"); + /* in the case of tethering we send + restriction only that must apply now */ + return true; + } + + if (key_quota->iftype == RESOURCED_IFACE_DATACALL) { + /* TODO it could get_current_modem_imsi_hash, and + * it could be faster */ + imsi_hash = get_imsi_hash(get_current_modem_imsi()); + /* in redwood imsi could be null due absent telephony + * response */ + if (!check_imsi_hash(key_quota->imsi_hash, imsi_hash)) { + _D("imsi different"); + return true; + } + } + /* TODO the same check for current iftype, without it + * WiFi quota and datacall quota couldn't coexit */ + return false; +} + static gboolean check_and_apply_node(gpointer key, gpointer value, gpointer user_data) { struct quota *app_quota = value; struct quota_key *key_quota = key; - int64_t send_delta, rcv_delta; - struct daemon_opts *opts = (struct daemon_opts *)user_data; + struct counter_arg *carg = (struct counter_arg *)user_data; resourced_net_restrictions rst = { RESOURCED_STATE_UNKNOWN, - RESOURCED_IFACE_UNKNOWN }; + RESOURCED_IFACE_UNKNOWN,}; + int64_t send_delta = app_quota->send_quota - app_quota->sent_used_quota; + int64_t rcv_delta = app_quota->rcv_quota - app_quota->rcv_used_quota; + struct daemon_opts *opts; - /* do not check already applied quota*/ - if (app_quota->state == RESOURCED_QUOTA_APPLIED) - return FALSE; + ret_value_msg_if(!carg, FALSE, "Please provide valid carg argument!"); + + opts = carg->opts; - send_delta = app_quota->send_quota - app_quota->sent_used_quota; - rcv_delta = app_quota->rcv_quota - app_quota->rcv_used_quota; + if (skip_quota(key_quota, app_quota, send_delta, rcv_delta)) { + _D("no need to apply quota"); + return FALSE; + } - if (app_quota->send_quota <= 0 || app_quota->rcv_quota <= 0) - send_restriction_notification(key_quota->app_id); - else - _check_warning_threshold(send_delta, rcv_delta, app_quota, - key_quota->app_id); + _D("quota rcv: %" PRId64 ", send: %" PRId64 "", app_quota->rcv_quota, + app_quota->send_quota); + _D("delta rcv: %" PRId64 ", send: %" PRId64 "", rcv_delta, send_delta); - if (_is_under_restriction(send_delta, rcv_delta, key_quota->iftype, + /* gap guard part, block immediately if send/rcv_delta is less or + * equal zero */ + if (check_restriction_needed(send_delta, rcv_delta, key_quota->iftype, opts->update_period) && (key_quota->roaming == RESOURCED_ROAMING_UNKNOWN || - key_quota->roaming == get_roaming())) { - if (!strcmp(key_quota->app_id, TETHERING_APP_NAME) && - (send_delta > 0 || rcv_delta > 0)) - /* in the case of tethering we send - restriction only that must apply now */ - return FALSE; + key_quota->roaming == get_current_roaming())) { + data_usage_quota du_quota = {0}; /* use both for + warning/restriction noti */ + rst.rs_type = key_quota->ground; rst.send_limit = cast_restriction_limit(send_delta); rst.rcv_limit = cast_restriction_limit(rcv_delta); - rst.snd_warning_limit = _get_warning_limit( - rst.send_limit, app_quota->snd_warning_threshold); - rst.rcv_warning_limit = _get_warning_limit( - rst.rcv_limit, app_quota->rcv_warning_threshold); + rst.snd_warning_limit = get_warning_limit(send_delta, + rst.send_limit, app_quota->snd_warning_threshold); + rst.rcv_warning_limit = get_warning_limit(rcv_delta, + rst.rcv_limit, app_quota->rcv_warning_threshold); - _SD("Applying quota for %s, iftype %d", key_quota->app_id, - key_quota->iftype); + _SD("Applying gap quota for %s, iftype %d, ground", key_quota->app_id, + key_quota->iftype, key_quota->ground); rst.iftype = key_quota->iftype; - + rst.ifname = get_iftype_name(rst.iftype); + rst.roaming = key_quota->roaming; + + /* + * client request quota for background application or + * applications, lets create here background cgroup, + * we will put later processes in it + */ + if (key_quota->ground == RESOURCED_STATE_BACKGROUND) + create_net_background_cgroup(carg); + + /* we already checked in check_restriction_needed + * is it current imsi or not, + * just do not skip kernel op */ if (proc_keep_restriction(key_quota->app_id, - app_quota->quota_id, &rst, - RST_SET) == RESOURCED_ERROR_NONE) { - app_quota->state = RESOURCED_QUOTA_APPLIED; - _D("Restriction was applied successfully."); + app_quota->quota_id, &rst, + RST_SET, false) != RESOURCED_ERROR_NONE) { + _E("Failed to keep restriction!"); + return FALSE; } + + du_quota.snd_quota = app_quota->send_quota; + du_quota.rcv_quota = app_quota->rcv_quota; + du_quota.quota_type = key_quota->ground; + + /* + * in case of !rst.send_limit and !rst.rcv_limit + * restriction will come from fill_restriction nfacct handler + * */ + if (/*!rst.send_limit || */ !rst.rcv_limit) + send_restriction_notification(key_quota->app_id, &du_quota); + else if (/*!rst.snd_warning_limit ||*/!rst.rcv_warning_limit) + send_restriction_warn_notification(key_quota->app_id, &du_quota); + + app_quota->state = RESOURCED_QUOTA_APPLIED; + _D("Restriction was applied successfully."); + } return FALSE; /* continue iteration */ } -static void check_and_apply_quota(volatile struct daemon_opts *opts) +static void check_and_apply_quota(struct counter_arg *carg) { - g_tree_foreach(quotas, check_and_apply_node, (void *)opts); + g_tree_foreach(quotas, check_and_apply_node, (void *)carg); } struct update_all_arg { resourced_iface_type iftype; + char *imsi_hash; struct application_stat *app_stat; }; +static inline bool check_imsi_hash(const char *hash_a, const char *hash_b) +{ + if (hash_a && hash_b) + return !strcmp(hash_a, hash_b); + return hash_a == hash_b; /* both null */ +} + static gboolean update_pseudo_app_entry(gpointer key, gpointer value, gpointer user_data) { @@ -692,25 +954,38 @@ static gboolean update_pseudo_app_entry(gpointer key, update_all_arg *)user_data; const struct quota_key *qkey = (const struct quota_key *)key; + struct quota *total_quota = (struct quota *)value; + + if (time(0) < total_quota->start_time) { + _D("No need to update effective quota!"); + return FALSE; + } + + _D("app id %s", qkey->app_id); + _D("app ground %d", qkey->ground); + _D("app stat app_id %s", arg->app_stat->application_id); + _D("app stat ground %d", arg->app_stat->ground); /* handle case for network interfaces*/ if ((!strcmp(qkey->app_id, RESOURCED_ALL_APP) && (qkey->iftype == RESOURCED_IFACE_UNKNOWN || qkey->iftype == RESOURCED_IFACE_ALL || qkey->iftype == arg->iftype) && + (check_imsi_hash(qkey->imsi_hash, arg->imsi_hash)) && (qkey->roaming == RESOURCED_ROAMING_UNKNOWN || - qkey->roaming == arg->app_stat->is_roaming)) || - !strcmp(qkey->app_id, TETHERING_APP_NAME)) { - struct quota *total_quota = (struct quota *)value; + qkey->roaming == arg->app_stat->is_roaming) && + CHECK_BIT(qkey->ground, arg->app_stat->ground)) || + !strcmp(qkey->app_id, TETHERING_APP_NAME)) + { /* update it */ total_quota->sent_used_quota += arg->app_stat->delta_snd; total_quota->rcv_used_quota += arg->app_stat->delta_rcv; arg->app_stat->delta_snd = 0; arg->app_stat->delta_rcv = 0; - _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d ifindex %d\n", + _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d \n", total_quota->sent_used_quota, total_quota->rcv_used_quota, - arg->iftype, arg->app_stat->ifindex); - + arg->iftype); + _D("app id %s", qkey->app_id); } return FALSE; @@ -757,6 +1032,8 @@ static gboolean update_each_quota(gpointer key, gpointer value, .app_stat = app_stat }; struct quota_key qkey; + arg.imsi_hash = app_key->iftype == RESOURCED_IFACE_DATACALL ? + get_imsi_hash(app_key->imsi) : ""; /* We should handle cases of RESOURCED_ALL_APP or TETHERING_APP_NAME in separate way due it's not comming with statistics from kernel */ @@ -768,6 +1045,8 @@ static gboolean update_each_quota(gpointer key, gpointer value, qkey.app_id = app_stat->application_id; qkey.iftype = app_key->iftype; qkey.roaming = app_stat->is_roaming; + /* TODO following code could be a function */ + qkey.imsi_hash = app_key->iftype == RESOURCED_IFACE_DATACALL ? get_imsi_hash(app_key->imsi): ""; update_traffic_quota(&qkey, &app_stat->delta_snd, &app_stat->delta_rcv); return FALSE; @@ -789,7 +1068,7 @@ static void calculate_finish_time(struct quota *app_quota) if (!app_quota->real_start) app_quota->real_start = time(0); - app_quota->real_finish = _get_finish_time(app_quota->real_start, + app_quota->real_finish = get_finish_time(app_quota->real_start, app_quota->time_period); } @@ -822,7 +1101,8 @@ static void drop_restriction(const struct quota_key *qkey, struct quota *app_quo _SD("Removing restriction of quota for %s,%d", qkey->app_id, qkey->iftype); - if (remove_restriction_local(qkey->app_id, qkey->iftype) + if (remove_restriction_local(qkey->app_id, qkey->iftype, + app_quota->quota_id, qkey->imsi_hash, qkey->ground) == RESOURCED_ERROR_NONE) app_quota->state = RESOURCED_QUOTA_REVERTED; } @@ -868,31 +1148,108 @@ static void finalize_statement(sqlite3_stmt **stmt) } } -resourced_ret_c process_quota(struct application_stat_tree *apps, - volatile struct daemon_opts *opts) +resourced_ret_c process_quota(struct counter_arg *carg) { - /* For first initialization */ - static int quota_updated; + ret_value_msg_if(!carg, RESOURCED_ERROR_INVALID_PARAMETER, + "Please provide carg!"); - if (opts && opts->is_update_quota) { - const int error = _update_quotas(); - if (error) - return error; - quota_updated = 1; + execute_once { + const resourced_ret_c ret = load_quotas(); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "Failed to load quotas!"); } - actualize_quota_table(apps); + actualize_quota_table(carg->result); - check_and_apply_quota(opts); + check_and_apply_quota(carg); - /* finilize state */ - if (opts && opts->is_update_quota && quota_updated) { - opts->is_update_quota = 0; - quota_updated = 0; - } return RESOURCED_ERROR_NONE; } +struct quota_search_context { + int quota_id; + struct quota *quota; + struct quota_key *key; +}; + +static gboolean search_quota_cb(gpointer key, gpointer value, gpointer data) +{ + struct quota_search_context *ctx = (struct quota_search_context *)data; + struct quota *quota = (struct quota *)value; + /** + * quota id is uniqe, but not in key, because isn't used in + * checking quota + */ + if (ctx->quota_id == quota->quota_id) { + ctx->quota = quota; + ctx->key = key; + return TRUE; + } + return FALSE; +} + +static gboolean search_background_quota_cb(gpointer key, gpointer value, gpointer data) +{ + bool *background = (bool *)data; + struct quota *quota = (struct quota *)value; + struct quota_key *qkey = (struct quota_key *)key; + /** + * quota id is uniqe, but not in key, because isn't used in + * checking quota + */ + if (quota->state == RESOURCED_QUOTA_APPLIED && + qkey->ground == RESOURCED_STATE_BACKGROUND) { + *background = true; + return TRUE; + } + return FALSE; +} + +resourced_ret_c get_quota_by_id(const int quota_id, data_usage_quota *du_quota) +{ + struct quota_search_context ctx = {.quota_id = quota_id}; + execute_once { + if (!g_tree_nnodes(quotas)) + load_quotas(); + } + g_tree_foreach(quotas, search_quota_cb, &ctx); + if (ctx.key && ctx.quota) { + du_quota->snd_quota = ctx.quota->send_quota; + du_quota->rcv_quota = ctx.quota->rcv_quota; + du_quota->imsi = ctx.key->imsi_hash; + du_quota->quota_type = ctx.key->ground; + return RESOURCED_ERROR_NONE; + } + return RESOURCED_ERROR_FAIL; +} + +resourced_ret_c get_quota_by_appid(const char* app_id, const char *imsi_hash, + const resourced_iface_type iftype, resourced_roaming_type roaming, + data_usage_quota *du_quota, int *quota_id, resourced_state_t ground) +{ + struct quota *qt; + execute_once { + if (!g_tree_nnodes(quotas)) + load_quotas(); + } + + qt = find_quota_in_tree(app_id, iftype, roaming, imsi_hash, ground); + if (qt) { + du_quota->snd_quota = qt->send_quota; + du_quota->rcv_quota = qt->rcv_quota; + *quota_id = qt->quota_id; + return RESOURCED_ERROR_NONE; + } + return RESOURCED_ERROR_FAIL; +} + +bool get_background_quota(void) +{ + bool background = false; + g_tree_foreach(quotas, search_background_quota_cb, &background); + return background; +} + /** * Release statement */ diff --git a/src/network/datausage-quota.c b/src/network/datausage-quota.c index 60c09e76..fac0f76f 100644 --- a/src/network/datausage-quota.c +++ b/src/network/datausage-quota.c @@ -84,7 +84,7 @@ static resourced_ret_c send_quota_message(const char *interface, static resourced_ret_c send_create_quota_message(const char *app_id, const data_usage_quota *quota) { - char *params[10]; + char *params[11]; char snd_quota[MAX_DEC_SIZE(int64_t)], rcv_quota[MAX_DEC_SIZE(int64_t)]; snprintf(snd_quota, sizeof(snd_quota), "%" PRId64 "", quota->snd_quota); @@ -93,20 +93,21 @@ static resourced_ret_c send_create_quota_message(const char *app_id, serialize_params(params, ARRAY_SIZE(params), app_id, quota->time_period, snd_quota, rcv_quota, quota->snd_warning_threshold, quota->rcv_warning_threshold, quota->quota_type, quota->iftype, - *quota->start_time, quota->roaming_type); - return send_quota_message(RESOURCED_NETWORK_CREATE_QUOTA, "sdttdddddd", + *quota->start_time, quota->roaming_type, quota->imsi); + return send_quota_message(RESOURCED_NETWORK_CREATE_QUOTA, "sdttdddddds", params); } static resourced_ret_c send_remove_quota_message(const char *app_id, const resourced_iface_type iftype, - const resourced_roaming_type roaming_type) + const resourced_roaming_type roaming_type, + const char *imsi, const resourced_state_t ground) { - char *params[3]; + char *params[5]; serialize_params(params, ARRAY_SIZE(params), app_id, iftype, - roaming_type); - return send_quota_message(RESOURCED_NETWORK_REMOVE_QUOTA, "sdd", + roaming_type, imsi, ground); + return send_quota_message(RESOURCED_NETWORK_REMOVE_QUOTA, "sddsd", params); } @@ -125,7 +126,7 @@ API resourced_ret_c remove_datausage_quota( return RESOURCED_ERROR_INVALID_PARAMETER; return send_remove_quota_message(rule->app_id, rule->iftype, - rule->roaming); + rule->roaming, rule->imsi ? rule->imsi : "", rule->quota_type); } API resourced_ret_c remove_datausage_quota_by_iftype( @@ -188,7 +189,14 @@ API resourced_ret_c set_datausage_quota(const char *app_id, _SD("quota.rcv_quota = %lld", quota->rcv_quota); _SD("quota.quota_type = %d", quota->quota_type); _SD("quota.iftype = %d", quota->iftype); + _SD("quota->imsi = %s", quota->imsi); + _SD("quota->roaming_type = %d", quota->roaming_type); + _SD("quota->snd_warning_threshold = %d", quota->snd_warning_threshold); + _SD("quota->rcv_warning_threshold = %d", quota->rcv_warning_threshold); _SD("==============================="); + /* replace imsi to empty string if NULL was given*/ + if (!quota_to_send.imsi) + quota_to_send.imsi = ""; return send_create_quota_message(app_id, "a_to_send); } diff --git a/src/network/datausage-vconf-callbacks.c b/src/network/datausage-vconf-callbacks.c index ae9098a4..9ec94e19 100644 --- a/src/network/datausage-vconf-callbacks.c +++ b/src/network/datausage-vconf-callbacks.c @@ -34,6 +34,8 @@ #include "resourced.h" #include "settings.h" #include "trace.h" +#include "telephony.h" +#include "notification.h" #include <stdlib.h> #include <vconf.h> @@ -87,6 +89,16 @@ static void datausage_timer_change_cb(keynode_t *key, void *data) options->update_period = val; } +static void datausage_sim_change_cb(keynode_t *key, void *data) +{ + int val = vconf_keynode_get_int(key); + + _SD("key = %s, value = %d(int)\n", + vconf_keynode_get_name(key), val); + + check_and_clear_all_noti(); +} + void resourced_add_vconf_datausage_cb(struct counter_arg *carg) { _D("Add vconf datausage callbacks\n"); @@ -101,6 +113,9 @@ void resourced_add_vconf_datausage_cb(struct counter_arg *carg) vconf_notify_key_changed(RESOURCED_DATACALL_LOGGING_PATH, datacall_logging_change_cb, (void *)carg->opts); + vconf_notify_key_changed(VCONF_TELEPHONY_DEFAULT_DATA_SERVICE, + datausage_sim_change_cb, + NULL); } void resourced_remove_vconf_datausage_cb(void) @@ -113,4 +128,6 @@ void resourced_remove_vconf_datausage_cb(void) datausage_timer_change_cb); vconf_ignore_key_changed(RESOURCED_DATACALL_LOGGING_PATH, datacall_logging_change_cb); + vconf_ignore_key_changed(VCONF_TELEPHONY_DEFAULT_DATA_SERVICE, + datausage_sim_change_cb); } diff --git a/src/network/datausage-vconf-common.c b/src/network/datausage-vconf-common.c index 3d914b11..a932d0e3 100644 --- a/src/network/datausage-vconf-common.c +++ b/src/network/datausage-vconf-common.c @@ -45,16 +45,6 @@ resourced_ret_c restriction_check_limit_status(int *retval) return RESOURCED_ERROR_NONE; } -resourced_ret_c restriction_read_quota(int *quota) -{ - if (vconf_get_int(VCONFKEY_SETAPPL_DATA_LIMIT_INT, quota)) { - _E("vconf_get_int FAIL\n"); - return RESOURCED_ERROR_FAIL; - }; - - return RESOURCED_ERROR_NONE; -} - void restriction_set_status(int value) { int limit = RESTRICTION_STATE_INIT; diff --git a/src/network/db-guard.c b/src/network/db-guard.c new file mode 100644 index 00000000..5ed9b4dc --- /dev/null +++ b/src/network/db-guard.c @@ -0,0 +1,118 @@ +/* + * resourced + * + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * @file db-guard.c + * + * @desc This guard procedures are responsible for period db erasing + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include <data_usage.h> +#include <vconf/vconf.h> +#include <Ecore.h> + +#include "config.h" +#include "counter.h" +#include "macro.h" +#include "trace.h" + +#define VCONF_KEY_DB_ENTRIES_COUNT "db/private/resourced/network_db_entries" +#define ENTRY_SIZE 128 + +/* one hour */ +#define ERASE_TIMER_INTERVAL 3600 +/* 40 days */ +#define ERASE_INTERVAL 3600 * 24 * 40 +/* 50 Mb */ +#define DB_SIZE_THRESHOLD 1048576 * 50 + +static int db_entries; + +resourced_ret_c reset_data_usage_first_n_entries(int num); + +void change_db_entries_num_num(int num) +{ + db_entries += num; + if (vconf_set_int(VCONF_KEY_DB_ENTRIES_COUNT, db_entries)) + _E("Failed to set new db entries number"); +} + +static void check_erase_db_oversize(void) +{ + struct stat db_stat = {0}; + int del_entry = 0; + + ret_msg_if(stat(DATABASE_FULL_PATH, &db_stat), + "Failed to get statistics for %s errno %d", + DATABASE_FULL_PATH, errno); + if (db_stat.st_size < DB_SIZE_THRESHOLD) { + _D("Db truncation isn't required!"); + return; + } + /* get approximate number of entries for removing */ + del_entry = (db_stat.st_size - DB_SIZE_THRESHOLD) / ENTRY_SIZE; + ret_msg_if(reset_data_usage_first_n_entries(del_entry), + "Failed to remove first %d entries", del_entry); + change_db_entries_num_num(-del_entry); +} + +static void erase_old_entries(void) +{ + data_usage_reset_rule rule = { + .iftype = RESOURCED_IFACE_LAST_ELEM, + }; + resourced_tm_interval interval; + time_t until = time(0); + until -= ERASE_INTERVAL; + + interval.from = 0; + interval.to = until; + rule.interval = &interval; + _D("Reset datausage statistics till %s", asctime(localtime(&until))); + ret_msg_if(reset_data_usage(&rule), + "Failed to reset statistics"); +} + +static Eina_Bool erase_func_cb(void *user_data) +{ + check_erase_db_oversize(); + erase_old_entries(); + return ECORE_CALLBACK_RENEW; +} + +resourced_ret_c resourced_init_db_guard(struct counter_arg *carg) +{ + carg->erase_timer = ecore_timer_add(ERASE_TIMER_INTERVAL, + erase_func_cb, carg); + ret_value_msg_if(carg->erase_timer == NULL, RESOURCED_ERROR_FAIL, + "Failed to create timer"); + ret_value_msg_if(vconf_get_int(VCONF_KEY_DB_ENTRIES_COUNT, &db_entries), + RESOURCED_ERROR_FAIL, "Failed to get vconf %s value!", + VCONF_KEY_DB_ENTRIES_COUNT); + return RESOURCED_ERROR_NONE; +} + diff --git a/src/network/foreach.c b/src/network/foreach.c index 0afb3538..e5d49593 100644 --- a/src/network/foreach.c +++ b/src/network/foreach.c @@ -35,70 +35,97 @@ #define DATA_USAGE_FOR_PERIOD "select binpath, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, " \ - "sum(sent) as sent from statistics where time_stamp between ? and ? " \ - "group by binpath, is_roaming order by received desc" + "sum(sent) as sent, imsi, ground from statistics " \ + "where time_stamp between ? and ? " \ + "group by binpath, is_roaming, imsi order by received desc" #define DATA_USAGE_FOR_PERIOD_IFACE "select binpath, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, " \ - "sum(sent) as sent from statistics where time_stamp between ? and ? " \ - "and iftype=? group by binpath, is_roaming order by received desc" + "sum(sent) as sent, imsi, ground from statistics " \ + "where time_stamp between ? and ? " \ + "and iftype=? group by binpath, is_roaming, imsi order by received desc" #define DATA_USAGE_CHUNKS "select binpath, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, " \ - "sum(sent) as sent, time_stamp - time_stamp % ? as time_stamp " \ + "sum(sent) as sent, time_stamp - time_stamp % ? as time_stamp, imsi, "\ + "ground " \ "from statistics where time_stamp between ? and ? " \ - "group by binpath, time_stamp order by time_stamp" + "group by binpath, time_stamp, imsi order by time_stamp" #define DATA_USAGE_CHUNKS_IFACE "select binpath, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, " \ - "sum(sent) as sent, time_stamp as start_time, " \ + "sum(sent) as sent, imsi, ground, " \ "time_stamp - time_stamp % ? as time_stamp " \ "from statistics where time_stamp between ? and ? and iftype=?" \ - "group by binpath, time_stamp order by time_stamp" + "group by binpath, time_stamp, imsi order by time_stamp" #define DATA_USAGE_APP_DETAILS "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname from statistics where time_stamp between ? and ? " \ - "and binpath=? group by binpath, iftype, is_roaming order by iftype" + "ifname, imsi, ground from statistics where time_stamp between ? and ? " \ + "and binpath=? " \ + "group by binpath, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming " \ + "order by time_stamp, binpath, iftype, ifname, imsi, " \ + "hw_net_protocol_type, is_roaming" #define DATA_USAGE_APP_DETAILS_IFACE "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname from statistics where time_stamp between ? and ? " \ - "and binpath=? and iftype=?" + "ifname, imsi, ground from statistics where time_stamp between ? and ? " \ + "and binpath=? and iftype=?" \ + "group by hw_net_protocol_type, is_roaming, iftype, ifname, imsi " \ + "order by time_stamp, hw_net_protocol_type, is_roaming, iftype, "\ + "ifname, imsi" #define DATA_USAGE_CHUNKS_APP "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname, time_stamp - time_stamp % ? as time_stamp " \ - "from statistics where time_stamp between ? and ? and binpath = ? " \ - "group by iftype, time_stamp order by time_stamp, iftype" + "ifname, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \ + "from statistics " \ + "group by iftype, ifname, time_stamp, hw_net_protocol_type, is_roaming " \ + "order by time_stamp, iftype, ifname, hw_net_protocol_type, is_roaming" #define DATA_USAGE_CHUNKS_APP_IFACE "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname, time_stamp - time_stamp % ? as time_stamp " \ + "ifname, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \ "from statistics where time_stamp between ? and ? and binpath = ? " \ - "and iftype = ? group by time_stamp order by time_stamp" + "and iftype = ? " \ + "group by time_stamp, hw_net_protocol_type, is_roaming, " \ + "iftype, ifname, imsi " \ + "order by time_stamp, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming" #define DATA_USAGE_TOTAL "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname from statistics where time_stamp between ? and ? " \ - "group by iftype order by iftype, is_roaming" + "ifname, imsi, ground from statistics where time_stamp between ? and ? " \ + "group by iftype, ifname, imsi, hw_net_protocol_type, is_roaming " \ + "order by time_stamp, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming" #define DATA_USAGE_TOTAL_IFACE "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname from statistics where time_stamp between ? and ? " \ - "and iftype=?" + "ifname, imsi, ground from statistics where time_stamp between ? and ? " \ + "and iftype=? " \ + "group by hw_net_protocol_type, is_roaming, " \ + "iftype, ifname, imsi " \ + "order by time_stamp, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming" #define DATA_USAGE_CHUNKS_TOTAL "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname, time_stamp - time_stamp % ? as time_stamp " \ + "ifname, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \ "from statistics where time_stamp between ? and ? " \ - "group by iftype, time_stamp order by time_stamp, iftype" + "group by time_stamp, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming " \ + "order by time_stamp, iftype, ifname, imsi, hw_net_protocol_type, " \ + "is_roaming" #define DATA_USAGE_CHUNKS_TOTAL_IFACE "select iftype, hw_net_protocol_type, " \ "is_roaming, sum(received) as received, sum(sent) as sent, " \ - "ifname, time_stamp - time_stamp % ? as time_stamp " \ + "ifname, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \ "from statistics where time_stamp between ? and ? " \ - "and iftype = ? group by time_stamp order by time_stamp" + "and iftype = ? " \ + "group by time_stamp, hw_net_protocol_type, is_roaming, iftype, ifname, imsi " \ + "order by time_stamp, hw_net_protocol_type, is_roaming, iftype, " \ + "ifname, imsi" static sqlite3_stmt *data_usage_for_period; static sqlite3_stmt *data_usage_for_period_iface; @@ -185,7 +212,6 @@ API resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule, data_usage_info data; sqlite3_stmt *stm; resourced_ret_c result = RESOURCED_ERROR_NONE; - resourced_counters *cnt = &data.foreground.cnt; int rc; int pos = 1;/* running through positions where to bind parameters in the query */ @@ -198,7 +224,6 @@ API resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule, return RESOURCED_ERROR_DB_FAILED; } - memset(&data, 0, sizeof(data)); if (!rule || !info_cb) @@ -245,10 +270,12 @@ API resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule, data.app_id = (char *)sqlite3_column_text(stm, 0); data.hw_net_protocol_type = sqlite3_column_int(stm, 1); data.roaming = sqlite3_column_int(stm, 2); - cnt->incoming_bytes = sqlite3_column_int64(stm, 3); - cnt->outgoing_bytes = sqlite3_column_int64(stm, 4); + data.ground = sqlite3_column_int(stm, 6); + data.cnt.incoming_bytes = sqlite3_column_int64(stm, 3); + data.cnt.outgoing_bytes = sqlite3_column_int64(stm, 4); + data.imsi = (char *)sqlite3_column_text(stm, 5); if (rule->granularity) { - interval.from = sqlite3_column_int64(stm, 5); + interval.from = sqlite3_column_int64(stm, 7); interval.to = interval.from + rule->granularity; } @@ -286,8 +313,10 @@ static sqlite3_stmt **details_stms[] = { static sqlite3_stmt *select_statement(const char *app_id, const data_usage_selection_rule *rule) { - return *details_stms[is_iftype_defined(rule->iftype) | - (app_id ? 0 : 2) | (rule->granularity ? 4 : 0)]; + const int stm_index = is_iftype_defined(rule->iftype) | + (app_id ? 0 : 2) | (rule->granularity ? 4 : 0); + _D("stm index %d", stm_index); + return *details_stms[stm_index]; } API resourced_ret_c data_usage_details_foreach(const char *app_id, @@ -297,7 +326,6 @@ API resourced_ret_c data_usage_details_foreach(const char *app_id, data_usage_info data; sqlite3_stmt *stm; resourced_ret_c result = RESOURCED_ERROR_NONE; - resourced_counters *cnt = &data.foreground.cnt; int rc; int pos = 1;/* running through positions where to bind parameters in the query */ @@ -360,16 +388,15 @@ API resourced_ret_c data_usage_details_foreach(const char *app_id, data.iftype = sqlite3_column_int(stm, 0); data.hw_net_protocol_type = sqlite3_column_int(stm, 1); data.roaming = sqlite3_column_int(stm, 2); - cnt->incoming_bytes = sqlite3_column_int64(stm, 3); - cnt->outgoing_bytes = sqlite3_column_int64(stm, 4); + data.cnt.incoming_bytes = sqlite3_column_int64(stm, 3); + data.cnt.outgoing_bytes = sqlite3_column_int64(stm, 4); data.ifname = (char *)sqlite3_column_text(stm, 5); + data.imsi = (char *)sqlite3_column_text(stm, 6); if (rule->granularity) { - interval.from = sqlite3_column_int64(stm, 6); + interval.from = sqlite3_column_int64(stm, 7); interval.to = interval.from + rule->granularity; } - data.app_id = (char *)sqlite3_column_text(stm, 0); - if (info_cb(&data, user_data) == RESOURCED_CANCEL) rc = SQLITE_DONE; /* emulate end of data */ diff --git a/src/network/generic-netlink.c b/src/network/generic-netlink.c index a7f0e8e7..8a58372e 100644 --- a/src/network/generic-netlink.c +++ b/src/network/generic-netlink.c @@ -53,54 +53,11 @@ #define NESTED_MCAST_MAX 256 #define MAX_PAYLOAD 1024 /* maximum payload size */ -/** - * @desc accepts opaque pointer - * extracts command id - */ -inline int netlink_get_command(struct genl *nl_ans) -{ - return nl_ans->g.cmd; -} - uint32_t netlink_get_family(struct genl *nl_ans) { return nl_ans->n.nlmsg_type; } -static void fill_traf_stat_list(char *buffer, __u16 count, - traffic_stat_tree *stats) -{ - struct traffic_event *cur = (struct traffic_event *)buffer; - - while (count) { - struct traffic_stat *to_insert; - struct classid_iftype_key *key; - - to_insert = g_new(struct traffic_stat, 1); - if (!to_insert) { - _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat)); - return; - } - - key = g_new(struct classid_iftype_key, 1); - - if (!key) { - _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key)); - g_free((gpointer)to_insert); - return; - } - - to_insert->bytes = cur->bytes; - to_insert->ifindex = cur->ifindex; - key->classid = cur->sk_classid ? - cur->sk_classid : RSML_UNKNOWN_CLASSID; - key->iftype = get_iftype(cur->ifindex); - g_tree_insert((GTree *) stats, (gpointer)key, to_insert); - --count; - ++cur; - } -} - /* * Send netlink message to kernel */ @@ -473,70 +430,6 @@ int send_restriction(int sock, const pid_t pid, const int family_id, return r; } -static void _process_answer(struct netlink_serialization_params *params) -{ - struct genl *nl_ans = params->ans; - traffic_stat_tree *stats = params->stat_tree; - ssize_t remains; - char *buffer; - struct nlattr *first_na, *second_na; - int first_len; - int count = 0; - - remains = GENLMSG_PAYLOAD(&nl_ans->n); - if (remains <= 0) - return; - - /* parse reply message */ - first_na = (struct nlattr *)GENLMSG_DATA(nl_ans); - - /* inline nla_next() */ - first_len = NLA_ALIGN(first_na->nla_len); - - second_na = (struct nlattr *) ((char *) first_na + first_len); - remains -= first_len; - - /* but we need data_attr->nla_len */ - buffer = (char *) malloc((size_t)remains); - if (buffer == NULL) - return; - - if (first_na->nla_type == TRAF_STAT_COUNT) { - count = *(__u16 *) NLA_DATA(first_na); - memcpy(buffer, (char *) NLA_DATA(second_na), - second_na->nla_len); - } else { - _D("Expected attribute %d got %d", TRAF_STAT_COUNT, first_na->nla_type); - } - - if (count > 0) - fill_traf_stat_list(buffer, count, stats); - free(buffer); - -} - -netlink_serialization_command *netlink_create_command( - struct netlink_serialization_params *params) -{ - static netlink_serialization_command command = {0,}; - const int netlink_command = netlink_get_command(params->ans); - - command.params = *params; - - if (netlink_command == TRAF_STAT_C_GET_CONN_IN) { - command.deserialize_answer = _process_answer; - command.params.stat_tree = params->carg->in_tree; - } else if (netlink_command == TRAF_STAT_C_GET_PID_OUT) { - command.deserialize_answer = _process_answer; - command.params.stat_tree = params->carg->out_tree; - } else { - _E("Unknown command!"); - return NULL; - } - - return &command; -} - resourced_ret_c process_netlink_restriction_msg(const struct genl *ans, struct traffic_restriction *restriction, uint8_t *command) { diff --git a/src/network/iface-cb.c b/src/network/iface-cb.c index 6f2457fc..b538239b 100644 --- a/src/network/iface-cb.c +++ b/src/network/iface-cb.c @@ -227,6 +227,5 @@ void resourced_iface_finalize(void) ecore_main_fd_handler_del(iface_ecore_fd_handler); shutdown(iface_fd, 2); close(iface_fd); - finalize_iftypes(); g_list_free_full(ifcallbacks, free); } diff --git a/src/network/iface.c b/src/network/iface.c index 5823bfd1..e0631f87 100644 --- a/src/network/iface.c +++ b/src/network/iface.c @@ -49,11 +49,10 @@ static int iface_stat[RESOURCED_IFACE_LAST_ELEM - 1]; static GTree *iftypes; /* holds int key and value of type resourced_iface_type */ -static GTree *ifnames; /* for keeping ifype - interface name association */ -static pthread_rwlock_t iftypes_guard = PTHREAD_RWLOCK_INITIALIZER; -static pthread_rwlock_t ifnames_guard = PTHREAD_RWLOCK_INITIALIZER; +static GSList *ifnames; /* for keeping ifype - interface name association */ +static pthread_rwlock_t iftypes_guard = PTHREAD_RWLOCK_INITIALIZER; static const char *UEVENT_FMT = "/sys/class/net/%s/uevent"; static const char *DEVTYPE_KEY = "DEVTYPE"; @@ -71,6 +70,12 @@ struct iface_relation { char ifname[MAX_NAME_LENGTH]; }; +struct iface_status { + bool active; + char ifname[MAX_NAME_LENGTH]; + resourced_iface_type iftype; +}; + static gint compare_int(gconstpointer a, gconstpointer b, gpointer UNUSED userdata) { @@ -101,22 +106,42 @@ static void put_iftype_to_tree(GTree *iftypes_tree, int ifindex, int iftype) g_tree_replace(iftypes_tree, (gpointer)ifindex, new_value); } -static void put_ifname_to_tree(GTree *ifnames_tree, char *ifname, int iftype) +static void keep_ifname(GSList **ifnames_list, char *ifname, int iftype) { - int name_len = strlen(ifname) + 1; - gpointer new_value = (gpointer)malloc(name_len); - if (!new_value) { - _E("Malloc of put_ifname_to_tree failed\n"); - return; + GSList *iter; + bool found = false; + struct iface_status *value; + ret_msg_if (!ifnames_list || !ifname, "Please provide valid argument!"); + + gslist_for_each_item(iter, *ifnames_list) { + struct iface_status *cur = (struct iface_status *)iter->data; + if (cur->iftype == iftype && !strcmp(cur->ifname, ifname)) { + cur->active = true; + found = true; + } } - strncpy(new_value, ifname, name_len); - if (!ifnames_tree) { - free(new_value); - _E("Please provide valid argument!"); + if (found) return; + + _D("Add new entry into ifnames"); + value = (struct iface_status *)malloc( + sizeof(struct iface_status)); + + ret_msg_if (!value, "Can't allocate memory for iface_status\n"); + value->active = true; /* we're putting it => it's active now */ + value->iftype = iftype; + STRING_SAVE_COPY(value->ifname, ifname); + *ifnames_list = g_slist_prepend(*ifnames_list, value); +} + +static void reset_active_ifnames(GSList *ifnames_list) +{ + GSList *iter; + gslist_for_each_item(iter, ifnames_list) { + struct iface_status *value = (struct iface_status *)iter->data; + value->active = false; } - g_tree_replace(ifnames_tree, (gpointer)iftype, new_value); } static resourced_iface_type get_iftype_from_tree(GTree *iftypes_tree, int ifindex) @@ -247,7 +272,6 @@ int init_iftype(void) resourced_iface_type iftype; struct if_nameindex *ids = if_nameindex(); GTree *iftypes_next = create_iface_tree(); - GTree *ifnames_next = create_iface_tree(); if (ids == NULL) { _E("Failed to initialize iftype table! errno: %d, %s", @@ -263,16 +287,22 @@ int init_iftype(void) NET_INTERFACE_NAMES_FILE); } + reset_active_ifnames(ifnames); iface_stat_allowance(); for (i = 0; ids[i].if_index != 0; ++i) { if (!is_address_exists(ids[i].if_name)) continue; iftype = read_iftype(ids[i].if_name); + /* don't put unknown network interface into list */ + if (iftype == RESOURCED_IFACE_UNKNOWN) { + _D("unknown ifname %s, ifype %d", ids[i].if_name, iftype); + continue; + } put_iftype_to_tree(iftypes_next, ids[i].if_index, iftype); /* we know here iftype/ids[i].if_name, lets populate * ifnames_tree */ - put_ifname_to_tree(ifnames_next, ids[i].if_name, iftype); + keep_ifname(&ifnames, ids[i].if_name, iftype); _D("ifname %s, ifype %d", ids[i].if_name, iftype); } @@ -280,14 +310,13 @@ int init_iftype(void) if_freenameindex(ids); reset_tree(iftypes_next, &iftypes, &iftypes_guard); - reset_tree(ifnames_next, &ifnames, &ifnames_guard); return RESOURCED_ERROR_NONE; } void finalize_iftypes(void) { reset_tree(NULL, &iftypes, &iftypes_guard); - reset_tree(NULL, &ifnames, &ifnames_guard); + g_slist_free_full(ifnames, free); g_slist_free_full(ifnames_relations, free); } @@ -314,77 +343,83 @@ resourced_iface_type convert_iftype(const char *buffer) return RESOURCED_IFACE_UNKNOWN; } -int is_allowed_ifindex(int ifindex) +int is_counting_allowed(resourced_iface_type iftype) { - return iface_stat[get_iftype(ifindex)]; + return iface_stat[iftype]; } -resourced_iface_type get_iftype(int ifindex) +API resourced_iface_type get_iftype(int ifindex) { return get_iftype_from_tree(iftypes, ifindex); } -static gboolean print_ifname(gpointer key, gpointer value, gpointer data) +static char *lookup_ifname(GSList *ifnames_list, int iftype) { - _D("ifname %s", (char *)value); - return FALSE; -} - -static char *get_ifname_from_tree(GTree *ifnames_tree, int iftype) -{ - char *ret = NULL; + GSList *iter; - ret_value_msg_if(!ifnames_tree, NULL, "Please provide valid argument!"); + ret_value_msg_if(!ifnames_list, NULL, "Please provide valid argument!"); - pthread_rwlock_rdlock(&ifnames_guard); - ret = (char *)g_tree_lookup(ifnames_tree, (gpointer)iftype); - pthread_rwlock_unlock(&ifnames_guard); - if (ret == NULL) - g_tree_foreach(ifnames_tree, print_ifname, NULL); + gslist_for_each_item(iter, ifnames_list) { + struct iface_status *value = (struct iface_status *)iter->data; + if (value->iftype == iftype) + return value->ifname; + } - return ret; + return NULL; } char *get_iftype_name(resourced_iface_type iftype) { - return get_ifname_from_tree(ifnames, iftype); -} - -static gboolean search_loopback(gpointer key, - gpointer value, - gpointer data) -{ - int *res = (int *)data; - if (!value) - return FALSE; - *res = *(int *)value == RESOURCED_IFACE_UNKNOWN ? TRUE : FALSE; - return *res; + return lookup_ifname(ifnames, iftype); } -static bool is_only_loopback(GTree *iftypes_tree) +resourced_iface_type get_iftype_by_name(char *name) { - int nodes = g_tree_nnodes(iftypes_tree); - int res = 0; + GSList *iter; + ret_value_msg_if(name == NULL, RESOURCED_IFACE_UNKNOWN, + "Invalid argument"); - if (nodes > 1) - return false; + gslist_for_each_item(iter, ifnames) { + struct iface_status *value = (struct iface_status *)iter->data; + if (!strcmp(value->ifname, name)) + return value->iftype; + } - g_tree_foreach(iftypes_tree, search_loopback, &res); - return res; + return RESOURCED_IFACE_UNKNOWN; } +/* now used only in ./src/network/ktgrabber-restriction.c:285 */ void for_each_ifindex(ifindex_iterator iter, void(*empty_func)(void *), void *data) { pthread_rwlock_rdlock(&iftypes_guard); - if (!is_only_loopback(iftypes)) - g_tree_foreach(iftypes, (GTraverseFunc)iter, data); - else if (empty_func) + g_tree_foreach(iftypes, (GTraverseFunc)iter, data); + + if (empty_func) empty_func(data); pthread_rwlock_unlock(&iftypes_guard); } +void for_each_ifnames(ifnames_iterator iter_cb, void(*empty_func)(void *), + void *data) +{ + GSList *iter; + gslist_for_each_item(iter, ifnames) { + struct iface_status *value = (struct iface_status *)iter->data; + /* as before invoke cb only for active interfaces */ + if (!value->active) + continue; + + if (iter_cb(value->iftype, value->ifname, data) == TRUE) + break; + } + + if (empty_func) + empty_func(data); + +} + void set_wifi_allowance(const resourced_option_state wifi_option) { iface_stat[RESOURCED_IFACE_WIFI] = wifi_option == RESOURCED_OPTION_ENABLE ? 1 : 0; @@ -394,3 +429,4 @@ void set_datacall_allowance(const resourced_option_state datacall_option) { iface_stat[RESOURCED_IFACE_DATACALL] = datacall_option == RESOURCED_OPTION_ENABLE ? 1 : 0; } + diff --git a/src/common/app-stat.h b/src/network/include/app-stat.h index 6818fadd..090e77e9 100644 --- a/src/common/app-stat.h +++ b/src/network/include/app-stat.h @@ -33,6 +33,7 @@ #include <sys/types.h> #include "const.h" +#include "config.h" #include "data_usage.h" #include "daemon-options.h" #include "transmission.h" @@ -55,28 +56,31 @@ struct application_stat { uint32_t delta_snd; uint32_t delta_rcv; +#ifndef CONFIG_DATAUSAGE_NFACCT pid_t pid; int ifindex; +#endif resourced_roaming_type is_roaming; -}; -/* -* Structure for holding serialized data from kernel @see traffic_event -*/ -struct traffic_stat { - unsigned long bytes; - int ifindex; + /* foreground/background state is here, + * not in classid_iftype_key, it means + * we'll not able to handle simultaneously + * counter per one application for background and + * foreground withing one counting cycle, + * so every time application goes to background/foreground + * we'll request its counter update */ + resourced_state_t ground; }; struct classid_iftype_key { u_int32_t classid; int iftype; - char ifname[MAX_NAME_LENGTH]; + /* pointer to telephony's imsi */ + char *imsi; + char ifname[MAX_IFACE_LENGTH]; }; -typedef GTree traffic_stat_tree; - struct application_stat_tree { GTree *tree; time_t last_touch_time; @@ -87,13 +91,16 @@ struct application_stat_tree *create_app_stat_tree(void); void free_app_stat_tree(struct application_stat_tree *tree); void nulify_app_stat_tree(struct application_stat_tree **tree); -traffic_stat_tree *create_traffic_stat_tree(void); -void free_traffic_stat_tree(traffic_stat_tree *list); +struct counter_arg; +#ifdef CONFIG_DATAUSAGE_NFACCT +void fill_nfacct_result(char *cnt_name, uint64_t bytes, + struct counter_arg *carg); +#else +/* It's not same function used at netacct and it's only used at ktgrabber. */ +void fill_app_stat_result(int ifindex, int classid, uint64_t bytes, int iotype, + struct counter_arg *carg); +#endif -resourced_ret_c prepare_application_stat(traffic_stat_tree *tree_in, - traffic_stat_tree *tree_out, - struct application_stat_tree *result, - volatile struct daemon_opts *opts); #endif /* _RESOURCED_APPLICATION_STAT_H_ */ diff --git a/src/network/include/counter.h b/src/network/include/counter.h index 3e89fdce..b05bb5d8 100644 --- a/src/network/include/counter.h +++ b/src/network/include/counter.h @@ -33,6 +33,8 @@ #include <Ecore.h> +#define RESOURCED_BACKGROUND_APP_NAME "BACKGROUND" + struct counter_arg { int sock; int ans_len; @@ -46,13 +48,19 @@ struct counter_arg { int noti_fd; Ecore_Fd_Handler *noti_fd_handler; #endif + int serialized_counters; /* number of counters which was serialized in + current request */ struct daemon_opts *opts; struct application_stat_tree *result; - traffic_stat_tree *in_tree; - traffic_stat_tree *out_tree; + time_t last_run_time; + /* main timer for getting kernel counters */ Ecore_Timer *ecore_timer; + /* handler for kernel's fd for getting counters from ktgrabber/nfacct */ Ecore_Fd_Handler *ecore_fd_handler; + /* timer for separate obtaining values from kernel and store result into db */ Ecore_Timer *store_result_timer; + /* timer for reset old statistics */ + Ecore_Timer *erase_timer; }; /** diff --git a/src/network/include/datausage-common.h b/src/network/include/datausage-common.h index 49b8cf9a..b626d025 100644 --- a/src/network/include/datausage-common.h +++ b/src/network/include/datausage-common.h @@ -32,6 +32,8 @@ #include <resourced.h> +#include "counter.h" +#include "nfacct-rule.h" #include "iface.h" enum netstat_control_type { @@ -61,5 +63,28 @@ iface_callback *create_counter_callback(void); struct nfacct_rule; void keep_counter(struct nfacct_rule *counter); +/* remove counter from tree and execute its rule */ +void finalize_counter(struct nfacct_rule *counter); + +void set_finalize_flag(struct nfacct_rule *counter); +void update_counter_quota_value(struct nfacct_rule *counter, uint64_t bytes); +void extract_restriction_list(struct counter_arg *arg, GSList **rst_list); +resourced_state_t get_app_ground(struct nfacct_rule *counter); + +/** + * @desc mark appropriate nfacct by app_id as background + */ +void mark_background(const char *app_id); + +/** + * @desc move all pids of existing nfacct from background cgroup, + * to appropriate apps cgroups + */ +void foreground_apps(struct counter_arg *carg); + +/** + * @desc move all pids of existing nfacct to background cgroup + */ +void background_apps(struct counter_arg *carg); #endif /* __RESOURCED_NETSTAT_COMMON_H__ */ diff --git a/src/network/include/datausage-quota-processing.h b/src/network/include/datausage-quota-processing.h index 016520ab..c4d867fe 100644 --- a/src/network/include/datausage-quota-processing.h +++ b/src/network/include/datausage-quota-processing.h @@ -30,21 +30,34 @@ #define _TRESOURCED_DATAUSAGE_QUOTA_PROCESSING_H_ #include <sqlite3.h> +#include <stdbool.h> -#include "app-stat.h" -#include "resourced.h" +#include "data_usage.h" + +struct serialization_quota { + int time_period; + int64_t snd_quota; + int64_t rcv_quota; + int snd_warning_threshold; + int rcv_warning_threshold; + resourced_state_t quota_type; + resourced_iface_type iftype; + time_t start_time; + resourced_roaming_type roaming_type; + char *imsi_hash; +}; /* * Store data in effective quota */ void flush_quota_table(void); +struct counter_arg; /* * Quota processing. It's apply quota if needed. * And actualize current quotas state. */ -resourced_ret_c process_quota(struct application_stat_tree *apps, - volatile struct daemon_opts *opts); +resourced_ret_c process_quota(struct counter_arg *carg); /* * Finish working with quotas @@ -54,8 +67,30 @@ void finalize_quotas(void); /* * Delete quota and drop remove restriction */ -void update_quota_state(const char *app_id, const resourced_iface_type iftype, - const time_t start_time, const int time_period, - const resourced_roaming_type roaming); +void update_quota_state(const char *app_id, const int quota_id, + struct serialization_quota *ser_quota); + +void remove_quota_from_counting(const char *app_id, const resourced_iface_type iftype, + const resourced_roaming_type roaming, + const char *imsi); + +void clear_effective_quota(const char *app_id, + const resourced_iface_type iftype, + const resourced_roaming_type roaming, + const char *imsi_hash); + +resourced_ret_c get_quota_by_id(const int quota_id, data_usage_quota *du_quota); +resourced_ret_c get_quota_by_appid(const char* app_id, const char *imsi_hash, + const resourced_iface_type iftype, resourced_roaming_type roaming_type, + data_usage_quota *du_quota, int *quota_id, resourced_state_t ground); +/** + * @desc return true if we have applied background quota + */ +bool get_background_quota(void); + + +bool check_quota_applied(const char *app_id, const resourced_iface_type iftype, + const resourced_roaming_type roaming, const char *imsi, + const resourced_state_t ground, int *quota_id); #endif /* _TRESOURCED_DATAUSAGE_QUOTA_PROCESSING_H_ */ diff --git a/src/network/include/datausage-restriction.h b/src/network/include/datausage-restriction.h index e1ee46a0..21d5d12c 100644 --- a/src/network/include/datausage-restriction.h +++ b/src/network/include/datausage-restriction.h @@ -28,6 +28,8 @@ #define _RESOURCED_RESTRICTION_H_ #include <sqlite3.h> +#include <stdbool.h> + #include "resourced.h" #include "data_usage.h" #include "transmission.h" @@ -42,7 +44,8 @@ resourced_ret_c update_restriction_db( const int rcv_limit, const int snd_limit, const resourced_restriction_state rst_state, const int quota_id, - const resourced_roaming_type roaming); + const resourced_roaming_type roaming, + const char *ifname); /** * @desc Get restriction info from database @@ -59,17 +62,23 @@ resourced_ret_c get_restriction_info(const char *app_id, resourced_ret_c process_kernel_restriction( const u_int32_t classid, const resourced_net_restrictions *rst, - const enum traffic_restriction_type rst_type); + const enum traffic_restriction_type rst_type, + const int quota_id); resourced_ret_c proc_keep_restriction( const char *app_id, int quota_id, const resourced_net_restrictions *rst, - const enum traffic_restriction_type rst_type); + const enum traffic_restriction_type rst_type, + bool skip_kernel_op); resourced_ret_c remove_restriction_local(const char *app_id, - const resourced_iface_type iftype); + const resourced_iface_type iftype, + const int quota_id, + const char *imsi, + const resourced_state_t ground); resourced_ret_c exclude_restriction_local(const char *app_id, const int quota_id, - const resourced_iface_type iftype); + const resourced_iface_type iftype, + const char *imsi); #endif /* _RESOURCED_RESTRICTION_H_ */ diff --git a/src/network/include/datausage-vconf-common.h b/src/network/include/datausage-vconf-common.h index 5d23d769..3e1a870a 100644 --- a/src/network/include/datausage-vconf-common.h +++ b/src/network/include/datausage-vconf-common.h @@ -25,5 +25,4 @@ enum restriction_state { }; resourced_ret_c restriction_check_limit_status(int *retval); -resourced_ret_c restriction_read_quota(int *quota); void restriction_set_status(int value); diff --git a/src/network/include/roaming.h b/src/network/include/db-guard.h index d527fd92..4c52f4e4 100644 --- a/src/network/include/roaming.h +++ b/src/network/include/db-guard.h @@ -1,7 +1,7 @@ /* * resourced * - * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,25 +18,21 @@ */ /* + * @file db-guard.h * - * @file roaming.h + * @desc This guard procedures are responsible for period db erasing + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. * - * @desc Roaming persistent object. Due roaming changes not so often we can keep it in - * our memory and handle roaming changes. */ -#ifndef _RSML_LIBS_ROAMING_H -#define _RSML_LIBS_ROAMING_H - -#include "data_usage.h" +#ifndef _RESOURCED_DB_GUARD_H_ +#define _RESOURCED_DB_GUARD_H_ -/** - * @brief Just get roaming state. - */ -resourced_roaming_type get_roaming(void); +void change_db_entries_num_num(int num); -typedef void(*roaming_cb)(void); +struct counter_arg; +resourced_ret_c resourced_init_db_guard(struct counter_arg *carg); -void regist_roaming_cb(roaming_cb cb); +#endif /* _RESOURCED_DB_GUARD_H_ */ -#endif /* _RSML_LIBS_ROAMING_H*/ diff --git a/src/network/include/iface.h b/src/network/include/iface.h index 3e70d0ef..05737d0f 100644 --- a/src/network/include/iface.h +++ b/src/network/include/iface.h @@ -43,10 +43,13 @@ typedef struct { int init_iftype(void); void finalize_iftypes(void); -int is_allowed_ifindex(int ifindex); - +/* TODO remove ktgrabber */ resourced_iface_type get_iftype(int ifindex); + +int is_counting_allowed(resourced_iface_type iftype); + char *get_iftype_name(resourced_iface_type iftype); +resourced_iface_type get_iftype_by_name(char *name); bool is_address_exists(const char *name); resourced_iface_type convert_iftype(const char *buffer); @@ -54,12 +57,19 @@ resourced_iface_type convert_iftype(const char *buffer); void set_wifi_allowance(const resourced_option_state wifi_option); void set_datacall_allowance(const resourced_option_state datacall_option); +/* TODO remove it when ktgrabber solution will be removed */ typedef int(*ifindex_iterator)(int ifindex, resourced_iface_type iftype, void *data); void for_each_ifindex(ifindex_iterator iter, void(*empty_func)(void *), void *data); +typedef int(*ifnames_iterator)(resourced_iface_type iftype, char *ifname, + void *data); + +void for_each_ifnames(ifnames_iterator iter, void(*empty_func)(void *), + void *data); + typedef GList iface_callbacks; #endif /*TRESOURCED_LIBS_NET_IFACE_H_*/ diff --git a/src/network/include/net-cls-cgroup.h b/src/network/include/net-cls-cgroup.h index 70eec153..1ec70506 100644 --- a/src/network/include/net-cls-cgroup.h +++ b/src/network/include/net-cls-cgroup.h @@ -66,12 +66,11 @@ char *get_app_id_by_classid(const u_int32_t classid, const bool update_state); u_int32_t get_classid_by_app_id(const char *app_id, int create); /** - * @desc take classid from net_cls cgroup with name pkg_name - * @param pkg_name - name of the cgroup - * @param create - in case of true - create cgroup if it's not exists - * @return classid + * @desc create cgroup, generate classid and put classid into cgroup */ -u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create); +resourced_ret_c make_net_cls_cgroup(const char *pkg_name, u_int32_t classid); + +resourced_ret_c place_pids_to_net_cgroup(const int pid, const char *pkg_name); /** * @desc Make net_cls cgroup and put in it the given pid and @@ -83,4 +82,11 @@ u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create); resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_name); +struct counter_arg; +/** + * @desc this function makes net_cls cgroup and put pids into it + * */ +void create_net_background_cgroup(struct counter_arg *carg); + + #endif /*_RESOURCED_NET_CLS_CGROUP_H_*/ diff --git a/src/network/include/netlink-restriction.h b/src/network/include/netlink-restriction.h index 1df4f035..53175314 100644 --- a/src/network/include/netlink-restriction.h +++ b/src/network/include/netlink-restriction.h @@ -39,6 +39,7 @@ * rst_type - type of restriction on the basis of which the restriction * can be applied, removed or excluded. * classid - id, that generated for each application in the cgroup + * quota_id - quota_id to store in nf_cntr tree * iftype - network interface type to proccess restriction * send_limit - amount number of engress bytes allowed for restriction * rcv_limit - amount number of ingress bytes allowed for restriction @@ -46,10 +47,11 @@ * rcv_warning_limit - threshold for warning notification on ingress bytes */ int send_net_restriction(const enum traffic_restriction_type rst_type, - const u_int32_t classid, + const u_int32_t classid, const int quota_id, const resourced_iface_type iftype, const int send_limit, const int rcv_limit, const int snd_warning_threshold, - const int rcv_warning_threshold); + const int rcv_warning_threshold, + const char *ifname); #endif /* RESOURCED_NET_RESTRICTION_H_ */ diff --git a/src/network/include/nfacct-rule.h b/src/network/include/nfacct-rule.h index 16b00e1d..1886bdc8 100644 --- a/src/network/include/nfacct-rule.h +++ b/src/network/include/nfacct-rule.h @@ -61,12 +61,29 @@ typedef enum { NFACCT_COUNTER, NFACCT_WARN, NFACCT_BLOCK, + NFACCT_TETH_COUNTER, NFACCT_RULE_LAST_ELEM, } nfacct_rule_intend; +enum nfnl_acct_flags { + NFACCT_F_QUOTA_PKTS = (1 << 0), + NFACCT_F_QUOTA_BYTES = (1 << 1), + NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */ +}; + +/* it's better to have + * base nfacct_rule with following fields: + * name, ifname, pid, classid, iftype, intend, carg, iptables_rule + * + * and inherited nfacct_rule_counter and nfacct_rule_restriction + * with additional field: + * quota, quota_id, roaming, rst_state + * + * But ANSI C doesn't support inheritance. + */ struct nfacct_rule { char name[MAX_NAME_LENGTH]; - char ifname[MAX_NAME_LENGTH]; + char ifname[MAX_IFACE_LENGTH]; pid_t pid; u_int32_t classid; @@ -76,6 +93,9 @@ struct nfacct_rule { struct counter_arg *carg; resourced_ret_c(*iptables_rule)(struct nfacct_rule *counter); u_int64_t quota; + int quota_id; + resourced_roaming_type roaming; + resourced_restriction_state rst_state; }; struct counter_arg; @@ -83,7 +103,11 @@ struct counter_arg; void generate_counter_name(struct nfacct_rule *counter); bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *counter); -resourced_ret_c nfacct_send_get(struct counter_arg *carg); +resourced_ret_c nfacct_send_get_all(struct counter_arg *carg); +resourced_ret_c nfacct_send_get_counters(struct counter_arg *carg, + const char *name); +resourced_ret_c nfacct_send_get(struct nfacct_rule *rule); +resourced_ret_c nfacct_send_del(struct nfacct_rule *counter); resourced_ret_c nfacct_send_initiate(struct counter_arg *carg); resourced_ret_c exec_iptables_cmd(const char *cmd_buf, pid_t *pid); diff --git a/src/network/include/nl-helper.h b/src/network/include/nl-helper.h index 0ef417b8..709b512f 100644 --- a/src/network/include/nl-helper.h +++ b/src/network/include/nl-helper.h @@ -98,7 +98,7 @@ struct genl { }; struct netlink_serialization_params { - traffic_stat_tree *stat_tree; + int direction; struct genl *ans; struct counter_arg *carg; int (*eval_attr)(struct rtattr *attr_list[__NFACCT_MAX], diff --git a/src/network/include/notification.h b/src/network/include/notification.h index fb28122f..688b9242 100644 --- a/src/network/include/notification.h +++ b/src/network/include/notification.h @@ -1,7 +1,7 @@ /* * resourced * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,8 +29,29 @@ #ifndef _RESOURCED_DATAUSAGE_NOTIFICATION_H #define _RESOURCED_DATAUSAGE_NOTIFICATION_H -void send_restriction_notification(const char *appid); -void send_restriction_warn_notification(const char *appid); +#include "data_usage.h" -#endif /* _RESOURCED_DATAUSAGE_NOTIFICATION_H */ +/* NOTI. */ +#define WARNING_NOTI_ON "WarningNotiOn" +#define WARNING_NOTI_OFF "WarningNotiOff" +#define DISABLE_NOTI_ON "DisabledNotiOn" +#define DISABLE_NOTI_OFF "DisabledNotiOff" + + +/* POPUP */ +#define POPUP_KEY "_SYSPOPUP_CONTENT_" +#define POPUP_KEY_LIMIT "_DATAUSAGE_LIMIT_" +#define POPUP_VALUE_DISABLED "datausage_disabled" +#define POPUP_VALUE_WARNING "datausage_warning" +#define METHOD_CALL_POPUP "DatausagePopupLaunch" +enum noti_type { + WARNING_NOTI, + DISABLE_NOTI, +}; + +void check_and_clear_all_noti(void); +void send_restriction_notification(const char *appid, data_usage_quota *du_quota); +void send_restriction_warn_notification(const char *appid, data_usage_quota *du_quota); + +#endif /* _RESOURCED_DATAUSAGE_NOTIFICATION_H */ diff --git a/src/network/include/protocol-info.h b/src/network/include/protocol-info.h deleted file mode 100644 index 3a8d4acd..00000000 --- a/src/network/include/protocol-info.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * resourced - * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * @file protocol-info.h - * - * @desc Network protocol entity: now it's only for - * datacall network interface type - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - */ - -#ifndef RESOURCED_PROTOCOL_INFO_NET_IFACE_H_ -#define RESOURCED_PROTOCOL_INFO_NET_IFACE_H_ - -#include "data_usage.h" - -void init_hw_net_protocol_type(void); - -void finalize_hw_net_protocol_type(void); - -resourced_hw_net_protocol_type get_hw_net_protocol_type( - const resourced_iface_type iftype); - -#endif /* RESOURCED_PROTOCOL_INFO_NET_IFACE_H_ */ diff --git a/src/network/include/restriction-handler.h b/src/network/include/restriction-handler.h index 28a72975..2fcd4cc9 100644 --- a/src/network/include/restriction-handler.h +++ b/src/network/include/restriction-handler.h @@ -28,7 +28,7 @@ #define _RESOURCED_RESTRICTION_HANDLER_H_ #include "iface.h" -#include "roaming.h" +#include "telephony.h" /** * @brief This function allocates structure @@ -37,12 +37,6 @@ */ iface_callback *create_restriction_callback(void); -/** - * @brief This function returns pointer to roaming - * callback. No need to free memory. - */ -roaming_cb get_roaming_restriction_cb(void); - void reactivate_restrictions(void); typedef GList list_restrictions_info; diff --git a/src/network/include/storage.h b/src/network/include/storage.h index 97fc9b31..9f0b6f36 100644 --- a/src/network/include/storage.h +++ b/src/network/include/storage.h @@ -47,10 +47,9 @@ resourced_ret_c init_database(const char *filename); /** * @desc Store result list to database. * @param stats - List of resolved application information - * @param flush_period - Time interval for storing data * @return 1 if flushed, 0 if not */ -int store_result(struct application_stat_tree *stats, int flush_period); +int store_result(struct application_stat_tree *stats); /** * @desc Just close sqlite statements. diff --git a/src/network/include/telephony.h b/src/network/include/telephony.h new file mode 100644 index 00000000..7d6ee922 --- /dev/null +++ b/src/network/include/telephony.h @@ -0,0 +1,49 @@ +/* + * resourced + * + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * + * @file telephony.h + * + * @desc Roaming persistent object. Due roaming changes not so often we can keep it in + * our memory and handle roaming changes. + */ + +#ifndef __RESOURCED_TELEPHONY_H +#define __RESOURCED_TELEPHONY_H + +#include <stdbool.h> +#include "data_usage.h" + +#define VCONF_TELEPHONY_DEFAULT_DATA_SERVICE "db/telephony/dualsim/default_data_service" + +resourced_roaming_type get_current_roaming(void); +resourced_hw_net_protocol_type get_current_protocol(resourced_iface_type iftype); + +/** + * @brief Get international mobile subscriber identity from saved list for current modem + */ +char *get_current_modem_imsi(void); +char *get_imsi_hash(char *imsi); +bool check_event_in_current_modem(const char *imsi_hash, + const resourced_iface_type iftype); + +void finilize_telephony(void); + +#endif /* __RESOURCED_TELEPHONY_H */ diff --git a/src/network/include/tethering-restriction.h b/src/network/include/tethering-restriction.h index 047cc058..1b962401 100644 --- a/src/network/include/tethering-restriction.h +++ b/src/network/include/tethering-restriction.h @@ -29,6 +29,7 @@ #ifndef RESOURCED_TETHERING_RESTRICTION_H_ #define RESOURCED_TETHERING_RESTRICTION_H_ +#include "trace.h" #include "transmission.h" #define PATH_TO_PROC_IP_FORWARD "/proc/sys/net/ipv4/ip_forward" diff --git a/src/network/dummy_roaming.c b/src/network/join-dummy.c index c67fee65..cc816c7b 100644 --- a/src/network/dummy_roaming.c +++ b/src/network/join-dummy.c @@ -1,7 +1,7 @@ /* - * resourced + * resourced * - * Copyright (c) 2013 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,27 +17,10 @@ * */ -/* - * - * @file roaming.c - * - * @desc It's dummy implementation for none telephony case. - */ - +#include "resourced.h" #include "macro.h" -#include "roaming.h" -#include "trace.h" -/* for avoiding dependency in this file */ - -void regist_roaming_cb(roaming_cb UNUSED cb) +API resourced_ret_c join_app_performance(const char *app_id, const pid_t pid) { - _D("ROAMING ISN'T SUPPORTED, CHECK TELEPHONY MODULE"); + return RESOURCED_ERROR_NONE; } - -resourced_roaming_type get_roaming(void) -{ - _D("ROAMING ISN'T SUPPORTED, CHECK TELEPHONY MODULE"); - return RESOURCED_ROAMING_UNKNOWN; -} - diff --git a/src/network/ktgrabber-parser.c b/src/network/ktgrabber-parser.c new file mode 100644 index 00000000..791da0bf --- /dev/null +++ b/src/network/ktgrabber-parser.c @@ -0,0 +1,110 @@ +/* + * resourced + * + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * @file ktgrabber-parse.c + * + * @desc User space code for ktgrabber logic + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include "generic-netlink.h" +#include "genl.h" +#include "trace.h" + + +/** + * @desc accepts opaque pointer + * extracts command id + */ +static inline int netlink_get_command(struct genl *nl_ans) +{ + return nl_ans->g.cmd; +} + +static void fill_traf_stat_list(char *buffer, __u16 count, + struct netlink_serialization_params *params) +{ + struct traffic_event *cur = (struct traffic_event *)buffer; + + while (count) { + fill_app_stat_result(cur->ifindex, cur->sk_classid, + cur->bytes, params->direction, params->carg); + --count; + ++cur; + } +} + +static void _process_answer(struct netlink_serialization_params *params) +{ + struct genl *nl_ans = params->ans; + ssize_t remains; + char *buffer; + struct nlattr *first_na, *second_na; + int first_len; + int count = 0; + + remains = GENLMSG_PAYLOAD(&nl_ans->n); + if (remains <= 0) + return; + + /* parse reply message */ + first_na = (struct nlattr *)GENLMSG_DATA(nl_ans); + + /* inline nla_next() */ + first_len = NLA_ALIGN(first_na->nla_len); + + second_na = (struct nlattr *) ((char *) first_na + first_len); + remains -= first_len; + + /* but we need data_attr->nla_len */ + buffer = (char *) malloc((size_t)remains); + if (buffer == NULL) + return; + + if (first_na->nla_type == TRAF_STAT_COUNT) { + count = *(__u16 *) NLA_DATA(first_na); + memcpy(buffer, (char *) NLA_DATA(second_na), + second_na->nla_len); + } else { + _D("Expected attribute %d got %d", TRAF_STAT_COUNT, first_na->nla_type); + } + + if (count > 0) + fill_traf_stat_list(buffer, count, params); + free(buffer); + +} + +netlink_serialization_command *netlink_create_command( + struct netlink_serialization_params *params) +{ + static netlink_serialization_command command = { + .deserialize_answer = _process_answer, + 0,}; + + command.params = *params; + command.params.direction = netlink_get_command(params->ans); + + return &command; +} + + diff --git a/src/network/ktgrabber-restriction.c b/src/network/ktgrabber-restriction.c index aad8618d..94de128d 100644 --- a/src/network/ktgrabber-restriction.c +++ b/src/network/ktgrabber-restriction.c @@ -242,11 +242,12 @@ static resourced_ret_c init_restriction_context(void) } int send_net_restriction(const enum traffic_restriction_type rst_type, - const u_int32_t classid, + const u_int32_t classid, const int UNUSED quota_id, const resourced_iface_type iftype, const int send_limit, const int rcv_limit, const int snd_warning_threshold, - const int rcv_warning_threshold) + const int rcv_warning_threshold, + const char UNUSED *ifname) { struct nf_arg nfarg; diff --git a/src/network/main.c b/src/network/main.c index 9b056e4b..8c70e03a 100644 --- a/src/network/main.c +++ b/src/network/main.c @@ -76,6 +76,13 @@ API void libresourced_db_initialize_once(void) return; } + res = sqlite3_exec(database, "PRAGMA locking_mode = NORMAL", 0, 0, 0); + if (res != SQLITE_OK) { + _E("Can't set locking mode %s", sqlite3_errmsg(database)); + _E("Skip set busy handler."); + return; + } + /* Set how many times we'll repeat our attempts for sqlite_step */ if (sqlite3_busy_handler(database, resourced_db_busy, NULL) != SQLITE_OK) { _E("Couldn't set busy handler!"); diff --git a/src/network/net-cls-cgroup.c b/src/network/net-cls-cgroup.c index 15cde236..f3729c39 100644 --- a/src/network/net-cls-cgroup.c +++ b/src/network/net-cls-cgroup.c @@ -24,21 +24,24 @@ * */ +#include <dirent.h> +#include <glib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + #include "appid-helper.h" -#include "net-cls-cgroup.h" #include "cgroup.h" #include "const.h" +#include "counter.h" #include "data_usage.h" +#include "datausage-common.h" #include "errors.h" #include "file-helper.h" #include "macro.h" +#include "net-cls-cgroup.h" #include "trace.h" -#include <dirent.h> -#include <glib.h> -#include <stdio.h> -#include <string.h> - #define CUR_CLASSID_PATH "/tmp/cur_classid" #define CLASSID_FILE_NAME "/net_cls.classid" #define PATH_TO_NET_CGROUP_DIR "/sys/fs/cgroup/net_cls" @@ -80,12 +83,15 @@ static int place_classid_to_cgroup(const char *cgroup, const char *subdir, u_int32_t *classid) { char buf[MAX_PATH_LENGTH]; - u_int32_t generated_classid = produce_classid(); - if (classid) - *classid = generated_classid; + u_int32_t result_classid = (classid && *classid) ? *classid : + produce_classid(); + + /* set classid as out argument */ + if (classid && !*classid) + *classid = result_classid; snprintf(buf, sizeof(buf), "%s/%s", cgroup, subdir); - return cgroup_write_node(buf, CLASSID_FILE_NAME, generated_classid); + return cgroup_write_node(buf, CLASSID_FILE_NAME, result_classid); } static u_int32_t get_classid_from_cgroup(const char *cgroup, const char *subdir) @@ -154,42 +160,38 @@ populate_classids_with_pids(const char *dir_name_buf, size_t dir_name_buf_len, u_int32_t get_classid_by_app_id(const char *app_id, int create) { - char pkgname[MAX_PATH_LENGTH]; - extract_pkgname(app_id, pkgname, sizeof(pkgname)); - return get_classid_by_pkg_name(pkgname, create); -} - -API u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create) -{ int ret = 0; int exists; u_int32_t classid = RESOURCED_UNKNOWN_CLASSID; - if (!strcmp(pkg_name, RESOURCED_ALL_APP)) + if (!strcmp(app_id, RESOURCED_ALL_APP)) return RESOURCED_ALL_APP_CLASSID; - if (!strcmp(pkg_name, TETHERING_APP_NAME)) + if (!strcmp(app_id, TETHERING_APP_NAME)) return RESOURCED_TETHERING_APP_CLASSID; + if (!strcmp(app_id, RESOURCED_BACKGROUND_APP_NAME)) + return RESOURCED_FOREGROUND_APP_CLASSID; + /* just read */ if (!create) classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR, - pkg_name); + app_id); if (classid != RESOURCED_UNKNOWN_CLASSID) return classid; - ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name, + ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)app_id, &exists); if (ret) goto handle_error; if (exists) classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR, - pkg_name); + app_id); else ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR, - (char *)pkg_name, &classid); + (char *)app_id, &classid); if (ret) goto handle_error; @@ -201,7 +203,6 @@ API u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create) return RESOURCED_UNKNOWN_CLASSID; } - int update_classids(void) { DIR *dir; @@ -298,9 +299,9 @@ char *get_app_id_by_classid(const u_int32_t classid, const bool update_state) return get_app_id_by_classid_local(classid); } -API resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_name) +API resourced_ret_c make_net_cls_cgroup(const char *pkg_name, u_int32_t classid) { - int ret = 0; + resourced_ret_c ret = RESOURCED_ERROR_NONE; int exists = 0; if (pkg_name == NULL) { @@ -308,17 +309,44 @@ API resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_ return RESOURCED_ERROR_INVALID_PARAMETER; } - _SD("pkg: %s; pid: %d\n", pkg_name, pid); - ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name, &exists); ret_value_if(ret < 0, RESOURCED_ERROR_FAIL); if (!exists) { ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, - NULL); + classid ? &classid : NULL); ret_value_if(ret < 0, RESOURCED_ERROR_FAIL); } + return ret; +} + +API resourced_ret_c place_pids_to_net_cgroup(const int pid, const char *pkg_name) +{ + char child_buf[21 + MAX_DEC_SIZE(int) + MAX_DEC_SIZE(int)]; + snprintf(child_buf, sizeof(child_buf), PROC_TASK_CHILDREN, pid, pid); + + if (access(child_buf, F_OK)) { + _D("%s of %s is not existed", child_buf, pkg_name); + return place_pid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid); + } - return place_pid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid); + return place_pidtree_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid); } +API resourced_ret_c make_net_cls_cgroup_with_pid(const int pid, const char *pkg_name) +{ + resourced_ret_c ret = make_net_cls_cgroup(pkg_name, RESOURCED_UNKNOWN_CLASSID); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, "Can't create cgroup %s!", pkg_name); + _SD("pkg: %s; pid: %d\n", pkg_name, pid); + return place_pids_to_net_cgroup(pid, pkg_name); +} + +void create_net_background_cgroup(struct counter_arg *carg) +{ + resourced_ret_c ret = make_net_cls_cgroup(RESOURCED_BACKGROUND_APP_NAME, + RESOURCED_BACKGROUND_APP_CLASSID); + if (ret == RESOURCED_ERROR_NONE) + background_apps(carg); + else + _E("Could not support quota for background application"); +} diff --git a/src/network/network-dummy.c b/src/network/network-dummy.c deleted file mode 100644 index e6f0380d..00000000 --- a/src/network/network-dummy.c +++ /dev/null @@ -1,150 +0,0 @@ - /* - * resourced - * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * @file network.c - * - * @desc Entity for storing applications statistics - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - */ - -#include <stdio.h> - -#include "data_usage.h" -#include "datausage-restriction.h" -#include "macro.h" -#include "net-cls-cgroup.h" -#include "rd-network.h" -#include "resourced.h" - -API network_error_e network_set_option(const network_option_s *options) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_get_option(network_option_s *options) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_make_cgroup_with_pid(const int pid, - const char *pkg_name) -{ - return NETWORK_ERROR_NONE; -} - -API u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_set_restriction(const char *app_id, - const network_restriction_s *restriction) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_restriction_foreach(network_restriction_cb restriction_cb, - void *user_data) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_remove_restriction(const char *app_id) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_remove_restriction_by_iftype(const char *app_id, - const network_iface_e iftype) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_exclude_restriction(const char *app_id) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_exclude_restriction_by_iftype( - const char *app_id, const network_iface_e iftype) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_register_activity_cb(network_activity_cb activity_cb) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_join_app_performance(const char *app_id, const pid_t pid) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_update_statistics(void) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_foreach(const network_selection_rule_s *rule, - network_info_cb info_cb, void *user_data) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_details_foreach(const char *app_id, - network_selection_rule_s *rule, - network_info_cb info_cb, - void *user_data) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_reset(const network_reset_rule_s *rule) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_remove_quota( - const network_quota_reset_rule_s *rule) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_remove_quota_by_iftype( - const char *app_id, const network_iface_e iftype) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_set_quota(const char *app_id, - const network_quota_s *quota) -{ - return NETWORK_ERROR_NONE; -} - -API network_error_e network_get_restriction_state(const char *pkg_id, - network_iface_e iftype, network_restriction_state *state) -{ - *state = NETWORK_RESTRICTION_UNDEFINDED; - return NETWORK_ERROR_NONE; -} diff --git a/src/network/network.c b/src/network/network.c deleted file mode 100644 index 225e059f..00000000 --- a/src/network/network.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * resourced - * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * @file network.c - * - * @desc Entity for storing applications statistics - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - */ - -#include <stdio.h> - -#include "data_usage.h" -#include "datausage-restriction.h" -#include "macro.h" -#include "net-cls-cgroup.h" -#include "rd-network.h" -#include "resourced.h" - -API network_error_e network_set_option(const network_option_s *options) -{ - return (network_error_e)set_resourced_options((const resourced_options*)options); -} - -API network_error_e network_get_option(network_option_s *options) -{ - return (network_error_e)get_resourced_options((resourced_options*)options); -} - -API network_error_e network_make_cgroup_with_pid(const int pid, - const char *pkg_name) -{ - return (network_error_e)make_net_cls_cgroup_with_pid(pid, pkg_name); -} - -API u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create) -{ - return (network_error_e)get_classid_by_pkg_name(pkg_name, create); -} - -API network_error_e network_set_restriction(const char *app_id, - const network_restriction_s *restriction) -{ - return (network_error_e)set_net_restriction(app_id, - (const resourced_net_restrictions*)restriction); -} - -API network_error_e network_restriction_foreach(network_restriction_cb restriction_cb, - void *user_data) -{ - return (network_error_e)restrictions_foreach((resourced_restriction_cb)restriction_cb, user_data); -} - -API network_error_e network_remove_restriction(const char *app_id) -{ - return (network_error_e)remove_restriction(app_id); -} - -API network_error_e network_remove_restriction_by_iftype(const char *app_id, - const network_iface_e iftype) -{ - return (network_error_e)remove_restriction_by_iftype(app_id, (const resourced_iface_type)iftype); -} - -API network_error_e network_exclude_restriction(const char *app_id) -{ - return (network_error_e)exclude_restriction(app_id); -} - -API network_error_e network_exclude_restriction_by_iftype( - const char *app_id, const network_iface_e iftype) -{ - return (network_error_e)exclude_restriction_by_iftype( - app_id, (const resourced_iface_type)iftype); -} - -API network_error_e network_register_activity_cb(network_activity_cb activity_cb) -{ - return (network_error_e)register_net_activity_cb((net_activity_cb)activity_cb); -} - -API network_error_e network_join_app_performance(const char *app_id, const pid_t pid) -{ - return (network_error_e)join_app_performance(app_id, pid); -} - -API network_error_e network_update_statistics(void) -{ - return (network_error_e)resourced_update_statistics(); -} - -API network_error_e network_foreach(const network_selection_rule_s *rule, - network_info_cb info_cb, void *user_data) -{ - return (network_error_e)data_usage_foreach( - (const data_usage_selection_rule*)rule, - (data_usage_info_cb)info_cb, - user_data); -} - -API network_error_e network_details_foreach(const char *app_id, - network_selection_rule_s *rule, - network_info_cb info_cb, - void *user_data) -{ - return (network_error_e)data_usage_details_foreach(app_id, - (data_usage_selection_rule*)rule, - (data_usage_info_cb)info_cb, - user_data); -} - -API network_error_e network_reset(const network_reset_rule_s *rule) -{ - return (network_error_e)reset_data_usage((const data_usage_reset_rule*)rule); -} - -API network_error_e network_remove_quota( - const network_quota_reset_rule_s *rule) -{ - return (network_error_e)remove_datausage_quota( - (const struct datausage_quota_reset_rule*)rule); -} - -API network_error_e network_remove_quota_by_iftype( - const char *app_id, const network_iface_e iftype) -{ - return (network_error_e)remove_datausage_quota_by_iftype(app_id, - (const resourced_iface_type)iftype); -} - -API network_error_e network_set_quota(const char *app_id, - const network_quota_s *quota) -{ - return (network_error_e)set_datausage_quota(app_id, - (const data_usage_quota*)quota); -} - -API network_error_e network_get_restriction_state(const char *pkg_id, - network_iface_e iftype, network_restriction_state *state) -{ - return (network_error_e)get_restriction_state(pkg_id, - (const resourced_iface_type)iftype, - (resourced_restriction_state *)state); -} diff --git a/src/network/nf-restriction.c b/src/network/nf-restriction.c index e2704e92..fc26e88a 100644 --- a/src/network/nf-restriction.c +++ b/src/network/nf-restriction.c @@ -34,12 +34,23 @@ #include "netlink-restriction.h" #include "nfacct-rule.h" #include "resourced.h" +#include "restriction-helper.h" +#include "telephony.h" #include "trace.h" static resourced_ret_c apply_net_restriction(struct nfacct_rule *rule, const int send_limit, const int rcv_limit) { - nfacct_rule_jump jump = rule->intend == NFACCT_WARN ? NFACCT_JUMP_ACCEPT : + nfacct_rule_jump jump; + + /* block immediately */ + if (!rcv_limit) { /* for dual nfacct entity for restriction add || !send_limit */ + return produce_net_rule(rule, 0, 0, + NFACCT_ACTION_INSERT, NFACCT_JUMP_REJECT, + NFACCT_COUNTER_OUT); + } + + jump = rule->intend == NFACCT_WARN ? NFACCT_JUMP_ACCEPT : NFACCT_JUMP_REJECT; return produce_net_rule(rule, send_limit, rcv_limit, @@ -78,11 +89,12 @@ static resourced_ret_c exclude_net_restriction(struct nfacct_rule *rule) } resourced_ret_c send_net_restriction(const enum traffic_restriction_type rst_type, - const u_int32_t classid, + const u_int32_t classid, const int quota_id, const resourced_iface_type iftype, const int send_limit, const int rcv_limit, const int snd_warning_threshold, - const int rcv_warning_threshold) + const int rcv_warning_threshold, + const char *ifname) { int ret; struct shared_modules_data *m_data = get_shared_modules_data(); @@ -90,9 +102,11 @@ resourced_ret_c send_net_restriction(const enum traffic_restriction_type rst_typ struct nfacct_rule rule = { .name = {0}, .ifname = {0}, - 0, + .quota_id = quota_id, }; + rule.rst_state = convert_to_restriction_state(rst_type); + ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL, "Empty shared modules data"); carg = m_data->carg; @@ -102,10 +116,12 @@ resourced_ret_c send_net_restriction(const enum traffic_restriction_type rst_typ rule.classid = classid; rule.iftype = iftype; rule.carg = carg; + rule.roaming = get_current_roaming(); + STRING_SAVE_COPY(rule.ifname, ifname); if (rst_type == RST_SET) { - if (snd_warning_threshold || - rcv_warning_threshold) { + /* snd_warning_threshold && */ + if (rcv_warning_threshold) { rule.intend = NFACCT_WARN; ret = apply_net_restriction(&rule, snd_warning_threshold, rcv_warning_threshold); diff --git a/src/network/nfacct-rule.c b/src/network/nfacct-rule.c index c5e2355f..1acdde85 100644 --- a/src/network/nfacct-rule.c +++ b/src/network/nfacct-rule.c @@ -36,6 +36,7 @@ #include "const.h" #include "counter.h" +#include "datausage-common.h" #include "iface.h" #include "macro.h" #include "module-data.h" @@ -53,21 +54,22 @@ #define NFACCT_NAME_MOD " -m nfacct --nfacct-name %s" #define REJECT_RULE " -j REJECT" #define ACCEPT_RULE " -j ACCEPT" +#define OUT_RULE "OUTPUT" +#define IN_RULE "INPUT" +#define FORWARD_RULE "FORWARD" /* TODO idea to use the same rule both for BLOCK (REJECT) and WARNING (ACCEPT) */ #define RULE_APP_OUT "%s -w %s OUTPUT -o %s -m cgroup --cgroup %u %s %s" #define RULE_APP_IN "%s -w %s INPUT -i %s -m cgroup --cgroup %u %s %s" -#define RULE_IFACE_OUT "%s -w %s OUTPUT -o %s %s -m cgroup ! --cgroup 0 %s" -#define RULE_IFACE_IN "%s -w %s INPUT -i %s %s -m cgroup ! --cgroup 0 %s" -#define NFNL_SUBSYS_ACCT 7 +/* iptables -w [I/A/D] [OUTPUT/FORWARD/INPUT] -o/-i iface -m nfacct --nfacct-name name -j ACCEPT/REJECT */ + +#define RULE_IFACE_OUT "%s -w %s %s -o %s %s %s" +#define RULE_IFACE_IN "%s -w %s %s -i %s %s %s" + -enum nfnl_acct_flags { - NFACCT_F_QUOTA_PKTS = (1 << 0), - NFACCT_F_QUOTA_BYTES = (1 << 1), - NFACCT_F_OVERQUOTA = (1 << 2), /* can't be set from userspace */ -}; +#define NFNL_SUBSYS_ACCT 7 static void prepare_netlink_msg(struct genl *req, int type, int flag) { @@ -161,31 +163,71 @@ static resourced_ret_c nfacct_send_new(struct nfacct_rule *counter) return send_nfacct_request(counter->carg->sock, &req); } -static resourced_ret_c nfacct_send_del(struct nfacct_rule *counter) +resourced_ret_c nfacct_send_del(struct nfacct_rule *counter) { struct genl req; +#ifdef DEBUG_ENABLED + _D("send remove request for %s", counter->name); +#endif prepare_netlink_msg(&req, NFNL_MSG_ACCT_DEL, NLM_F_ACK); add_string_attr(&req, counter->name, NFACCT_NAME); return send_nfacct_request(counter->carg->sock, &req); } #define NFACCT_F_QUOTAS (NFACCT_F_QUOTA_BYTES | NFACCT_F_QUOTA_PKTS) -resourced_ret_c nfacct_send_get(struct counter_arg *carg) + +static resourced_ret_c internal_nfacct_send_get(struct counter_arg *carg, + enum nfnl_acct_msg_types get_type, const char *name, + int mask, int filter) { struct genl req; struct nlattr *na; - prepare_netlink_msg(&req, NFNL_MSG_ACCT_GET_CTRZERO, - NLM_F_DUMP); + int flag = !name ? NLM_F_DUMP : 0; + prepare_netlink_msg(&req, get_type, + flag); /* due we don't get counter with quota any where else, * here we will request just counters by default */ + if (name) + add_string_attr(&req, name, NFACCT_NAME); + na = start_nest_attr(&req, NFACCT_FILTER); - add_uint32_attr(&req, NFACCT_F_QUOTAS, + add_uint32_attr(&req, htonl(mask), NFACCT_FILTER_ATTR_MASK); - add_uint32_attr(&req, 0, NFACCT_FILTER_ATTR_VALUE); + add_uint32_attr(&req, htonl(filter), NFACCT_FILTER_ATTR_VALUE); end_nest_attr(&req, na); return send_nfacct_request(carg->sock, &req); } +resourced_ret_c nfacct_send_get_counters(struct counter_arg *carg, const char *name) +{ + /* get and reset countes value */ + return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET_CTRZERO, name, + NFACCT_F_QUOTAS, 0); +} + +resourced_ret_c nfacct_send_get_quotas(struct counter_arg *carg, const char *name) +{ + /* just get counters */ + return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET, name, NFACCT_F_QUOTA_BYTES, + NFACCT_F_QUOTA_BYTES); +} + +resourced_ret_c nfacct_send_get_all(struct counter_arg *carg) +{ + /* get and reset everything, used when quiting */ + return internal_nfacct_send_get(carg, NFNL_MSG_ACCT_GET_CTRZERO, NULL, 0, 0); +} + +resourced_ret_c nfacct_send_get(struct nfacct_rule *rule) +{ + if (rule->intend == NFACCT_BLOCK || rule->intend == NFACCT_WARN) + return nfacct_send_get_quotas(rule->carg, rule->name); + else if (rule->intend == NFACCT_COUNTER) + return nfacct_send_get_counters(rule->carg, rule->name); + + return RESOURCED_ERROR_INVALID_PARAMETER; +} + resourced_ret_c nfacct_send_initiate(struct counter_arg *carg) { struct genl req; @@ -212,8 +254,11 @@ bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *cnt) char *classid_part; char *io_part; char *ifname_part; + char name[MAX_NAME_LENGTH]; /* parse buffer to avoid cnt_name modification */ + + strncpy(name, cnt_name, sizeof(name)); - switch (cnt_name[0]) { + switch (name[0]) { case 'c': cnt->intend = NFACCT_COUNTER; break; @@ -223,11 +268,52 @@ bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *cnt) case 'r': cnt->intend = NFACCT_BLOCK; break; + case 't': + cnt->intend = NFACCT_TETH_COUNTER; + break; default: return false; } - io_part = strtok(cnt_name, "_"); + STRING_SAVE_COPY(cnt->name, cnt_name); + + if (cnt->intend == NFACCT_TETH_COUNTER) { + char ifname_buf[MAX_IFACE_LENGTH]; + int ifname_len; + resourced_iface_type iface; + /* tbnep+:seth_w0; means comes by bt go away by mobile interface, + * it's outgoing traffic, due all tethering is mobile databased */ + iftype_part = strchr(name, ':'); + ret_value_msg_if (iftype_part == NULL, + false, "Invalid format of the tethering counter %s", name); + ifname_len = iftype_part - name - 1; + strncpy(ifname_buf, name + 1, ifname_len); /* skip first t */ + ifname_buf[ifname_len] = '\0'; + iface = get_iftype_by_name(ifname_buf); + /* check first part is it datacall */ + if (iface == RESOURCED_IFACE_DATACALL) { + strcpy(cnt->ifname, ifname_buf); + cnt->iotype = NFACCT_COUNTER_IN; + } else { + strcpy(ifname_buf, iftype_part + 1); /* +1, due : symbol and + til the end of cnt_name */ + iface = get_iftype_by_name(ifname_buf); + if (iface == RESOURCED_IFACE_DATACALL) { + cnt->iotype = NFACCT_COUNTER_OUT; + strcpy(cnt->ifname, ifname_buf); + } + } + + if (cnt->iotype == NFACCT_COUNTER_UNKNOWN) { + _E("cant determine tethering direction %s", name); + return false; + } + cnt->iftype = RESOURCED_IFACE_DATACALL; + cnt->classid = RESOURCED_TETHERING_APP_CLASSID; + return true; + } + + io_part = strtok(name, "_"); if (io_part != NULL) cnt->iotype = convert_to_iotype(atoi(io_part + 1)); else @@ -313,10 +399,15 @@ static unsigned int get_args_number(const char *cmd_buf) return count; } -static void wait_for_rule_cmd(struct nfacct_rule *rule, pid_t pid) +static void wait_for_rule_cmd(pid_t pid) { int status; - pid_t ret_pid = waitpid(pid, &status, 0); + pid_t ret_pid; + if (!pid) { + _D("no need to wait"); + return; + } + ret_pid = waitpid(pid, &status, 0); if (ret_pid < 0) _D("can't wait for a pid %d %d %s", pid, status, strerror(errno)); } @@ -396,7 +487,7 @@ static char *choose_iftype_name(struct nfacct_rule *rule) } static resourced_ret_c exec_iface_cmd(const char *pattern, const char *cmd, - const char *nfacct, const char *jump, + const char *chain, const char *nfacct, const char *jump, char *iftype_name, pid_t *pid) { char block_buf[MAX_PATH_LENGTH]; @@ -405,9 +496,9 @@ static resourced_ret_c exec_iface_cmd(const char *pattern, const char *cmd, ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL, "Invalid network interface name argument"); - ret = sprintf(block_buf, pattern, IPTABLES, cmd, + ret = sprintf(block_buf, pattern, IPTABLES, cmd, chain, iftype_name, nfacct, jump); - ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE, + ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_FAIL, "Not enough buffer"); return exec_iptables_cmd(block_buf, pid); } @@ -423,7 +514,7 @@ static resourced_ret_c exec_app_cmd(const char *pattern, const char *cmd, "Invalid network interface name argument"); ret = sprintf(block_buf, pattern, IPTABLES, cmd, iftype_name, classid, nfacct, jump); - ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE, + ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_FAIL, "Not enough buffer"); return exec_iptables_cmd(block_buf, pid); } @@ -440,6 +531,16 @@ static char *get_iptables_cmd(const nfacct_rule_action action) return ""; } +static char *get_iptables_chain(const nfacct_rule_direction iotype) +{ + if (iotype == NFACCT_COUNTER_IN) + return IN_RULE; + else if(iotype == NFACCT_COUNTER_OUT) + return OUT_RULE; + + return ""; +} + static char *get_iptables_jump(const nfacct_rule_jump jump) { if (jump == NFACCT_JUMP_ACCEPT) @@ -473,15 +574,23 @@ static resourced_ret_c produce_app_rule(struct nfacct_rule *rule, * don't use it in case of just block without a limit * iow, send_limit = 0 and rcv_limit 0 */ if (action != NFACCT_ACTION_DELETE) { + ret = nfacct_send_del(rule); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "can't del quota counter"); + ret = nfacct_send_new(rule); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, "can't set nfacct counter"); + keep_counter(rule); } /* we have a counter, let's key in a rule, drop in case of * send_limit/rcv_limit */ ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD, rule->name); + ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0, RESOURCED_ERROR_FAIL, + "Not enought buffer"); + ret = exec_app_cmd(RULE_APP_IN, set_cmd, nfacct_buf, jump_cmd, rule->classid, choose_iftype_name(rule), &pid); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, @@ -490,16 +599,16 @@ static resourced_ret_c produce_app_rule(struct nfacct_rule *rule, rule->classid, set_cmd, jump_cmd); /* remove in any case */ - if (action != NFACCT_ACTION_APPEND) { + if (action == NFACCT_ACTION_DELETE) { /* TODO here and everywhere should be not just a del, * here should be get counted value and than * set new counter with that value, but it's minor issue, * due it's not clear when actual counters was stored, * and based on which value settings made such decition */ - wait_for_rule_cmd(rule, pid); - ret = nfacct_send_del(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set nfacct counter"); + wait_for_rule_cmd(pid); + rule->iptables_rule = nfacct_send_del; + set_finalize_flag(rule); + nfacct_send_get(rule); } } @@ -509,27 +618,36 @@ static resourced_ret_c produce_app_rule(struct nfacct_rule *rule, rule->quota = send_limit; generate_counter_name(rule); if (action != NFACCT_ACTION_DELETE) { + ret = nfacct_send_del(rule); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "can't del quota counter"); + ret = nfacct_send_new(rule); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, "can't set quota counter"); + keep_counter(rule); } - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set counter"); - ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD, rule->name); + ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0, RESOURCED_ERROR_FAIL, + "Not enought buffer"); + ret = exec_app_cmd(RULE_APP_OUT, set_cmd, nfacct_buf, jump_cmd, rule->classid, choose_iftype_name(rule), &pid); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL, "Can't set conditional block for engress" " traffic, for classid %u, cmd %s, j %s", rule->classid, set_cmd, jump_cmd); - if (action != NFACCT_ACTION_APPEND) { - wait_for_rule_cmd(rule, pid); - ret = nfacct_send_del(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't del nfacct counter"); + if (action == NFACCT_ACTION_DELETE) { + wait_for_rule_cmd(pid); + rule->iptables_rule = nfacct_send_del; + /* not effective, it's better to replace + * set_finalize_flag by set_property, + * due keep_counter it necessary only for + * setting iptables_rule */ + set_finalize_flag(rule); + nfacct_send_get(rule); } } return RESOURCED_ERROR_NONE; @@ -548,68 +666,86 @@ static resourced_ret_c produce_iface_rule(struct nfacct_rule *rule, resourced_ret_c ret; pid_t pid = 0; - if (iotype & NFACCT_COUNTER_IN) { - /* income part */ - rule->quota = rcv_limit; - rule->iotype = NFACCT_COUNTER_IN; - generate_counter_name(rule); + /* keep one name for all restriction always */ + rule->iotype = NFACCT_COUNTER_IN; + rule->quota = rcv_limit; + generate_counter_name(rule); - if (action != NFACCT_ACTION_DELETE) { - ret = nfacct_send_new(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set quota counter"); - } + if (action != NFACCT_ACTION_DELETE) { + /* send delete comman in case of creation, + * because nfacct doesn't reset value for nfacct quota + * in case of quota existing */ + ret = nfacct_send_del(rule); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "can't del quota counter"); + ret = nfacct_send_new(rule); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "can't set quota counter"); + keep_counter(rule); + } + + if (iotype & NFACCT_COUNTER_IN) { + /* income part */ ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD, rule->name); - ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL, + ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0, RESOURCED_ERROR_FAIL, "Not enought buffer"); - ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, nfacct_buf, - jump_cmd, choose_iftype_name(rule), &pid); + ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, get_iptables_chain(rule->iotype), + nfacct_buf, jump_cmd, choose_iftype_name(rule), &pid); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, - RESOURCED_ERROR_NONE, "Can't set conditional block for ingress" + RESOURCED_ERROR_FAIL, "Can't set conditional block for ingress" " traffic, for iftype %d, cmd %s, j %s", rule->iftype, set_cmd, jump_cmd); - if (action != NFACCT_ACTION_APPEND) { - wait_for_rule_cmd(rule, pid); - ret = nfacct_send_del(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set quota counter"); + /* for tethering */ + if (rule->intend == NFACCT_WARN || rule->intend == NFACCT_BLOCK) { + /* RULE_IFACE_OUT is not a misprint here */ + wait_for_rule_cmd(pid); + ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, FORWARD_RULE, nfacct_buf, + jump_cmd, choose_iftype_name(rule), &pid); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, + RESOURCED_ERROR_FAIL, "Can't set forward rule for ingress" + " traffic, for iftype %d, cmd %s, j %s", + rule->iftype, set_cmd, jump_cmd); } + /* tethering */ } if (iotype & NFACCT_COUNTER_OUT) { /* outcome part */ - rule->iotype = NFACCT_COUNTER_OUT; rule->quota = send_limit; - generate_counter_name(rule); - - if (action != NFACCT_ACTION_DELETE) { - ret = nfacct_send_new(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set quota counter"); - } ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD, rule->name); - ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL, + ret_value_msg_if(ret > sizeof(nfacct_buf) || ret < 0, RESOURCED_ERROR_FAIL, "Not enough buffer"); - ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, nfacct_buf, + wait_for_rule_cmd(pid); + ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, OUT_RULE, nfacct_buf, jump_cmd, choose_iftype_name(rule), &pid); ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL, "Can't set conditional block for " " engress traffic, for iftype %d, cmd %s, j %s", rule->iftype, set_cmd, jump_cmd); - - if (action != NFACCT_ACTION_APPEND) { - wait_for_rule_cmd(rule, pid); - ret = nfacct_send_del(rule); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "can't set quota counter"); + /* for tethering */ + if (rule->intend == NFACCT_WARN || rule->intend == NFACCT_BLOCK) { + wait_for_rule_cmd(pid); + ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, FORWARD_RULE, nfacct_buf, + jump_cmd, choose_iftype_name(rule), &pid); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, + RESOURCED_ERROR_FAIL, "Can't set forward rule for engress" + " traffic, for iftype %d, cmd %s, j %s", + rule->iftype, set_cmd, jump_cmd); } + /* tethering */ + } + if (action == NFACCT_ACTION_DELETE) { + wait_for_rule_cmd(pid); + rule->iptables_rule = nfacct_send_del; + set_finalize_flag(rule); + nfacct_send_get(rule); } return RESOURCED_ERROR_NONE; } @@ -640,22 +776,21 @@ resourced_ret_c produce_net_rule(struct nfacct_rule *rule, void generate_counter_name(struct nfacct_rule *counter) { char warn_symbol = 'c'; - char *iftype_name = get_iftype_name(counter->iftype); - ret_msg_if(iftype_name == NULL, "Can't get interface name!"); - STRING_SAVE_COPY(counter->ifname, iftype_name); + if (!strlen(counter->ifname)) { + char *iftype_name = get_iftype_name(counter->iftype); + /* trace counter name, maybe name was already generated */ + ret_msg_if(iftype_name == NULL, + "Can't get interface name for counter %s, iftype %d)!", + counter->name, counter->iftype); + STRING_SAVE_COPY(counter->ifname, iftype_name); + } if (counter->intend == NFACCT_WARN) warn_symbol = 'w'; else if (counter->intend == NFACCT_BLOCK) warn_symbol = 'r'; - if (counter->classid != RESOURCED_ALL_APP_CLASSID) - snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%d_%s", + snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%d_%s", warn_symbol, counter->iotype, counter->iftype, counter->classid, counter->ifname); - else - snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%s", - warn_symbol, counter->iotype, counter->iftype, - counter->ifname); - - } +} diff --git a/src/network/notification-mobile.c b/src/network/notification-mobile.c new file mode 100644 index 00000000..bfcb3353 --- /dev/null +++ b/src/network/notification-mobile.c @@ -0,0 +1,204 @@ +/* + * resourced + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * @file notification_mobile.c + * + * @desc Notification specific functions + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include <resourced.h> + +#include "edbus-handler.h" +#include "notification.h" +#include "trace.h" +#include "macro.h" +#include "telephony.h" +#include "datausage-vconf-common.h" + +#define RESTRICTION_ACTIVE "RestrictionActive" +#define RESTRICTION_WARNING "RestrictionWarning" +#define B_TO_MB (1024 * 1024) + +static int warning_noti_id = 0; +static int disable_noti_id = 0; + +static int *get_noti_id(int type) +{ + if (type == WARNING_NOTI) + return &warning_noti_id; + else if (type == DISABLE_NOTI) + return &disable_noti_id; + else + _E("No matched noti type: %d", type); + return NULL; +} + +static int call_datausage_noti(const char *method_name, char *sig, char *pa[]) +{ + DBusError err; + DBusMessage *msg; + int ret, ret_val; + int i = 0; + + do { + msg = dbus_method_sync(SYSTEM_POPUP_BUS_NAME, SYSTEM_POPUP_PATH_DATAUSAGE, SYSTEM_POPUP_IFACE_DATAUSAGE, method_name, sig, pa); + if (msg) + break; + _E("Re-try to sync DBUS message, err_count : %d", i); + } while (i++ < RETRY_MAX); + + if (!msg) { + _E("Failed to sync DBUS message."); + return -EBADMSG; + } + + dbus_error_init(&err); + + ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &ret_val, DBUS_TYPE_INVALID); + if (!ret) { + _E("no message : [%s:%s]\n", err.name, err.message); + ret_val = -EBADMSG; + } + dbus_message_unref(msg); + dbus_error_free(&err); + + return ret_val; +} + +static int clear_datausage_noti(int *id, const char *method_name) +{ + char buf[MAX_DEC_SIZE(int)]; + char *pa[1]; + int ret; + + ret_value_msg_if (!id || !method_name, -EINVAL, "Invalid param"); + + snprintf(buf, sizeof(buf), "%d", *id); + + pa[0] = buf; + ret = call_datausage_noti(method_name, "i", pa); + if(ret != 0) { + _E("clear noti id : %d", *id); + *id = 0; + return ret; + } + + return 0; +} + +void check_and_clear_all_noti(void) +{ + int *warning_id; + int *disable_id; + + /* remove warning noti. */ + warning_id = get_noti_id(WARNING_NOTI); + if (warning_id) + clear_datausage_noti(warning_id, WARNING_NOTI_OFF); + + /* remove disable noti. */ + disable_id = get_noti_id(DISABLE_NOTI); + if (disable_id) { + clear_datausage_noti(disable_id, DISABLE_NOTI_OFF); + restriction_set_status(RESTRICTION_STATE_UNSET); + } else + _D("No disable noti. to remove"); +} + +static int show_restriction_noti(const char *method_name) +{ + ret_value_msg_if (!method_name, -EINVAL, "Invalid param"); + + return call_datausage_noti(method_name, NULL, NULL); +} + +static int show_restriction_popup(const char *value, data_usage_quota *du_quota) +{ + char buf[MAX_DEC_SIZE(int)]; + char str_val[32]; + char *pa[4]; + int ret, retval, quota_limit = -1; + + ret_value_msg_if(!value || !du_quota, -EINVAL, "Invalid param"); + + if (restriction_check_limit_status(&retval) < 0) + _E("Failed to check limit status"); + + if (!retval) { + _E("data usage limit is not set"); + return RESOURCED_ERROR_FAIL; + } + + quota_limit = (int) du_quota->snd_quota / B_TO_MB; + + ret_value_msg_if(quota_limit <= 0, RESOURCED_ERROR_FAIL, "quota_limit is invalid: %d\n", quota_limit); + + snprintf(str_val, sizeof(str_val), "%s", value); + snprintf(buf, sizeof(buf), "%d", quota_limit); + + pa[0] = POPUP_KEY; + pa[1] = str_val; + pa[2] = POPUP_KEY_LIMIT; + pa[3] = buf; + + ret = dbus_method_async(SYSTEM_POPUP_BUS_NAME, SYSTEM_POPUP_PATH_DATAUSAGE, SYSTEM_POPUP_IFACE_DATAUSAGE, METHOD_CALL_POPUP, "ssss", pa); + if (ret < 0) + _E("no message : failed to setting %d", ret); + return ret; +} + +void send_restriction_notification(const char *appid, data_usage_quota *du_quota) +{ + if (broadcast_edbus_signal(RESOURCED_PATH_NETWORK, + RESOURCED_INTERFACE_NETWORK, + RESTRICTION_ACTIVE, + DBUS_TYPE_STRING, + (void *)(&appid)) != RESOURCED_ERROR_NONE) { + _E("Failed to send DBUS message."); + } + + restriction_set_status(RESTRICTION_STATE_SET); + + _I("Show a network disabled popup & noti."); + + if (warning_noti_id) + clear_datausage_noti(&warning_noti_id, WARNING_NOTI_OFF); + + show_restriction_popup(POPUP_VALUE_DISABLED, du_quota); + disable_noti_id = show_restriction_noti(DISABLE_NOTI_ON); +} + +void send_restriction_warn_notification(const char *appid, data_usage_quota *du_quota) +{ + if (broadcast_edbus_signal(RESOURCED_PATH_NETWORK, + RESOURCED_INTERFACE_NETWORK, + RESTRICTION_WARNING, + DBUS_TYPE_STRING, + (void *)(&appid)) != RESOURCED_ERROR_NONE) { + _E("Failed to send DBUS message."); + } + + _I("Show a network warning notification"); + + warning_noti_id = show_restriction_noti(WARNING_NOTI_ON); +} diff --git a/src/network/notification.c b/src/network/notification-wearable.c index f8f96b14..50faf554 100644 --- a/src/network/notification.c +++ b/src/network/notification-wearable.c @@ -1,7 +1,7 @@ /* * resourced * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. + * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ /* - * @file notification.c + * @file notification_wearable.c * * @desc Notification specific functions * @@ -32,20 +32,24 @@ #include "notification.h" #include "trace.h" #include "macro.h" -#include "roaming.h" +#include "telephony.h" #include "datausage-vconf-common.h" -#define RESTRICTION_ACTIVE "RestrictionActive" -#define RESTRICTION_WARNING "RestrictionWarning" +#define RESTRICTION_ACTIVE "RestrictionActive" +#define RESTRICTION_WARNING "RestrictionWarning" -#define NOTI_KEY "_SYSPOPUP_CONTENT_" -#define NOTI_KEY_LIMIT "_DATAUSAGE_LIMIT_" -#define NOTI_VALUE_DISABLED "datausage_disabled" -#define NOTI_VALUE_WARNING "datausage_warning" +#define NOTI_KEY "_SYSPOPUP_CONTENT_" +#define NOTI_KEY_LIMIT "_DATAUSAGE_LIMIT_" +#define NOTI_VALUE_DISABLED "datausage_disabled" +#define NOTI_VALUE_WARNING "datausage_warning" +#define METHOD_CALL_POPUP "DatausagePopupLaunch" -#define METHOD_CALL_POPUP "DatausagePopupLaunch" +void check_and_clear_all_noti(void) +{ + +} -static int show_restriction_popup(const char *value) +static int show_restriction_popup(const char *value, data_usage_quota *du_quota) { char buf[MAX_DEC_SIZE(int)]; char str_val[32]; @@ -60,9 +64,6 @@ static int show_restriction_popup(const char *value) return RESOURCED_ERROR_FAIL; } - if (restriction_read_quota("a_limit) < 0) - _E("Failed to read a quota value"); - if (quota_limit <= 0) { _D("quota_limit is invalid\n"); return RESOURCED_ERROR_FAIL; @@ -82,7 +83,7 @@ static int show_restriction_popup(const char *value) return ret; } -void send_restriction_notification(const char *appid) +void send_restriction_notification(const char *appid, data_usage_quota *du_quota) { if (broadcast_edbus_signal(RESOURCED_PATH_NETWORK, RESOURCED_INTERFACE_NETWORK, @@ -95,10 +96,10 @@ void send_restriction_notification(const char *appid) restriction_set_status(RESTRICTION_STATE_SET); _I("Show a network disabled popup"); - show_restriction_popup(NOTI_VALUE_DISABLED); + show_restriction_popup(NOTI_VALUE_DISABLED, du_quota); } -void send_restriction_warn_notification(const char *appid) +void send_restriction_warn_notification(const char *appid, data_usage_quota *du_quota) { if (broadcast_edbus_signal(RESOURCED_PATH_NETWORK, RESOURCED_INTERFACE_NETWORK, @@ -109,6 +110,6 @@ void send_restriction_warn_notification(const char *appid) } _I("Show a network warning popup"); - show_restriction_popup(NOTI_VALUE_WARNING); + show_restriction_popup(NOTI_VALUE_WARNING, du_quota); } diff --git a/src/network/protocol-info.c b/src/network/protocol-info.c deleted file mode 100644 index e166717c..00000000 --- a/src/network/protocol-info.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * resourced - * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * @file protocol-info.c - * - * @desc Network protocol entity: now it's only for - * datacall network interface type - * - * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - */ - -#include <vconf/vconf.h> - -#include "macro.h" -#include "protocol-info.h" -#include "resourced.h" -#include "trace.h" - -static resourced_hw_net_protocol_type datacall_prot_t = RESOURCED_PROTOCOL_NONE; - -static resourced_hw_net_protocol_type _convert_to_resourced_protocol( - const int prot_type) -{ - switch (prot_type) { - case VCONFKEY_TELEPHONY_SVCTYPE_NOSVC: - return RESOURCED_PROTOCOL_DATACALL_NOSVC; - case VCONFKEY_TELEPHONY_SVCTYPE_EMERGENCY: - return RESOURCED_PROTOCOL_DATACALL_EMERGENCY; - case VCONFKEY_TELEPHONY_SVCTYPE_SEARCH: - return RESOURCED_PROTOCOL_DATACALL_SEARCH; - case VCONFKEY_TELEPHONY_SVCTYPE_2G: - return RESOURCED_PROTOCOL_DATACALL_2G; - case VCONFKEY_TELEPHONY_SVCTYPE_2_5G: - return RESOURCED_PROTOCOL_DATACALL_2_5G; - case VCONFKEY_TELEPHONY_SVCTYPE_2_5G_EDGE: - return RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE; - case VCONFKEY_TELEPHONY_SVCTYPE_3G: - return RESOURCED_PROTOCOL_DATACALL_3G; - case VCONFKEY_TELEPHONY_SVCTYPE_HSDPA: - return RESOURCED_PROTOCOL_DATACALL_HSDPA; - case VCONFKEY_TELEPHONY_SVCTYPE_LTE: - return RESOURCED_PROTOCOL_DATACALL_LTE; - case VCONFKEY_TELEPHONY_SVCTYPE_NONE: - default: - return RESOURCED_PROTOCOL_NONE; - } -} - -static resourced_ret_c _get_protocol_type( - resourced_hw_net_protocol_type *prot_type) -{ - int ret, status; - - ret = vconf_get_int(VCONFKEY_TELEPHONY_SVCTYPE, &status); - ret_value_msg_if(ret != 0, RESOURCED_ERROR_FAIL, - "vconf get failed(VCONFKEY_TELEPHONY_SVCTYPE)\n"); - *prot_type = _convert_to_resourced_protocol(status); - return RESOURCED_ERROR_NONE; -} - -static void _datacall_protocol_type_change_cb(keynode_t *key, void *data) -{ - int val = vconf_keynode_get_int(key); - - _D("key = %s, value = %d(int)\n", vconf_keynode_get_name(key), val); - datacall_prot_t = _convert_to_resourced_protocol(val); -} - -void init_hw_net_protocol_type(void) -{ - vconf_notify_key_changed(VCONFKEY_TELEPHONY_SVCTYPE, - _datacall_protocol_type_change_cb, NULL); - if (_get_protocol_type(&datacall_prot_t) != RESOURCED_ERROR_NONE) - _E("_get_protocol_type failed\n"); -} - -void finalize_hw_net_protocol_type(void) -{ - vconf_ignore_key_changed(VCONFKEY_TELEPHONY_SVCTYPE, - _datacall_protocol_type_change_cb); - datacall_prot_t = RESOURCED_PROTOCOL_NONE; -} - -resourced_hw_net_protocol_type get_hw_net_protocol_type( - const resourced_iface_type iftype) -{ - if (iftype == RESOURCED_IFACE_DATACALL) - return datacall_prot_t; - - return RESOURCED_PROTOCOL_NONE; -} diff --git a/src/network/reset.c b/src/network/reset.c index 766a1a07..c2ec0bed 100644 --- a/src/network/reset.c +++ b/src/network/reset.c @@ -44,11 +44,14 @@ #define RESET_APP_IFACE "delete from statistics where binpath=? and " \ "iftype=? and time_stamp between ? and ?" +#define RESET_FIRST_BY_NUMBER "delete from statistics where time_stamp in " \ + "(select time_stamp from statistics desc limit ?)" + /* the following array is strictly ordered * to find required statement the following code will be used: * (app ? 1 : 0) | (iftype ? 2 : 0) */ -static sqlite3_stmt *reset_stms[4]; +static sqlite3_stmt *reset_stms[5]; #define PREPARE(stm, query) do { \ rc = sqlite3_prepare_v2(db, query, -1, &stm, NULL); \ @@ -73,6 +76,7 @@ static int init_datausage_reset(sqlite3 *db) PREPARE(reset_stms[1], RESET_APP); PREPARE(reset_stms[2], RESET_IFACE); PREPARE(reset_stms[3], RESET_APP_IFACE); + PREPARE(reset_stms[4], RESET_FIRST_BY_NUMBER); initialized = 1; return rc; @@ -93,6 +97,33 @@ void finalize_datausage_reset(void) FINALIZE(reset_stms[i]); } +API resourced_ret_c reset_data_usage_first_n_entries(int num) +{ + resourced_ret_c result = RESOURCED_ERROR_NONE; + + ret_value_msg_if (!num, RESOURCED_ERROR_INVALID_PARAMETER, + "Invalid number of entries"); + libresourced_db_initialize_once(); + + if (init_datausage_reset(resourced_get_database()) != SQLITE_OK) { + _D("Failed to initialize data usage reset statements: %s\n", + sqlite3_errmsg(resourced_get_database())); + return RESOURCED_ERROR_DB_FAILED; + } + if (sqlite3_bind_int(reset_stms[4], 1, num) != SQLITE_OK) { + result = RESOURCED_ERROR_DB_FAILED; + goto out; + } + if (sqlite3_step(reset_stms[4]) != SQLITE_DONE) { + _D("Failed to drop collected statistics."); + result = RESOURCED_ERROR_DB_FAILED; + } +out: + sqlite3_reset(reset_stms[4]); + return result; +} + + API resourced_ret_c reset_data_usage(const data_usage_reset_rule *rule) { sqlite3_stmt *stm; diff --git a/src/network/restriction-handler.c b/src/network/restriction-handler.c index 2d086645..7d18766c 100644 --- a/src/network/restriction-handler.c +++ b/src/network/restriction-handler.c @@ -29,15 +29,19 @@ #include <data_usage.h> #include <stdlib.h> #include <net/if.h> +#include <inttypes.h> #include "const.h" +#include "datausage-quota-processing.h" +#include "datausage-restriction.h" #include "iface.h" #include "macro.h" +#include "module-data.h" #include "net-cls-cgroup.h" -#include "trace.h" -#include "restriction-helper.h" -#include "datausage-restriction.h" +#include "notification.h" #include "restriction-handler.h" +#include "restriction-helper.h" +#include "trace.h" struct restriction_context { int ifindex; @@ -101,6 +105,25 @@ struct apply_param enum restriction_apply_type apply_type; }; +static bool check_current_imsi_for_restriction(resourced_iface_type iftype, + int quota_id) +{ + data_usage_quota du_quota = {0}; + resourced_ret_c ret; + + if (iftype != RESOURCED_IFACE_DATACALL) + return false; + + ret = get_quota_by_id(quota_id, &du_quota); + if (ret == RESOURCED_ERROR_NONE && du_quota.imsi) { + const char *imsi_hash = get_imsi_hash(get_current_modem_imsi()); + _SD("current imsi %s", imsi_hash); + _SD("restrictions imsi %s", du_quota.imsi); + return imsi_hash && strcmp(du_quota.imsi, imsi_hash); + } + return false; +} + static void _reset_restrictions_iter(gpointer data, gpointer user_data) { resourced_restriction_info *arg = (resourced_restriction_info *)data; @@ -118,9 +141,44 @@ static void _reset_restrictions_iter(gpointer data, gpointer user_data) rst.rcv_limit = arg->rcv_limit; rst.roaming = arg->roaming; - if (param->apply_type == KEEP_AS_IS) + if (param->apply_type == KEEP_AS_IS) { + data_usage_quota du_quota = {0}; + + if (check_current_imsi_for_restriction(arg->iftype, arg->quota_id)) { + _D("It's restriction for another SIM"); + return; + } rst_type = convert_to_restriction_type(arg->rst_state); - else if (param->apply_type == UNSET) + + get_quota_by_id(arg->quota_id, &du_quota); + + if (du_quota.quota_type == RESOURCED_STATE_BACKGROUND) { + struct shared_modules_data *m_data; + struct counter_arg *carg; + + m_data = get_shared_modules_data(); + ret_msg_if(m_data == NULL, "Can't get module data!"); + + carg = m_data->carg; + ret_msg_if(carg == NULL, "Cant' get counter arg!"); + + create_net_background_cgroup(carg); + } + + /* !rst.send_limit || is needed in dual counter model */ + if (arg->quota_id && !rst.rcv_limit) { + _D("quota rcv: % " PRId64 ", send: % " PRId64 " ", du_quota.rcv_quota, du_quota.snd_quota); + + send_restriction_notification(arg->app_id, &du_quota); + } else if(arg->quota_id && rst.rcv_warning_limit) { + get_quota_by_id(arg->quota_id, &du_quota); + _D("quota rcv: % " PRId64 ", send: % " PRId64 " ", du_quota.rcv_quota, du_quota.snd_quota); + + send_restriction_warn_notification(arg->app_id, &du_quota); + } + + /* here we need to request sync get/update of restriction */ + } else if (param->apply_type == UNSET) rst_type = RST_UNSET; else rst_type = RST_UNDEFINDED; @@ -128,7 +186,7 @@ static void _reset_restrictions_iter(gpointer data, gpointer user_data) app_classid = get_classid_by_app_id(arg->app_id, false); error_code = process_kernel_restriction(app_classid, - &rst, rst_type); + &rst, rst_type, arg->quota_id); ret_msg_if(error_code != RESOURCED_ERROR_NONE, "restriction type %d failed, error %d\n", rst_type, @@ -210,24 +268,7 @@ static void handle_on_iface_down(const int ifindex) } _reset_restrictions(context.restrictions); _free_reset_restrictions(context.restrictions); -} - -static resourced_cb_ret roaming_restrictions_iter( - const resourced_restriction_info *info, void *user_data) -{ - struct apply_param param = {.apply_type = KEEP_AS_IS}; - _reset_restrictions_iter((gpointer)info, ¶m); - return RESOURCED_CONTINUE; -} - -static void handle_roaming_change(void) -{ - restrictions_foreach(roaming_restrictions_iter, NULL); -} - -roaming_cb get_roaming_restriction_cb(void) -{ - return handle_roaming_change; + check_and_clear_all_noti(); } iface_callback *create_restriction_callback(void) diff --git a/src/network/restriction-helper.c b/src/network/restriction-helper.c index 609b8c07..5edc7186 100644 --- a/src/network/restriction-helper.c +++ b/src/network/restriction-helper.c @@ -31,10 +31,9 @@ resourced_iface_type get_store_iftype(const u_int32_t app_classid, const resourced_iface_type iftype) { - /* We need to put RESOURCED_IFACE_ALL type into the database, - in case of the "tethering" because it with no iftype */ + /* in general tethering is based on datacall interface */ return (app_classid == RESOURCED_TETHERING_APP_CLASSID) ? - RESOURCED_IFACE_ALL : iftype; + RESOURCED_IFACE_DATACALL : iftype; } resourced_restriction_state convert_to_restriction_state( diff --git a/src/network/restriction-local.c b/src/network/restriction-local.c index 6356300f..3388cfee 100644 --- a/src/network/restriction-local.c +++ b/src/network/restriction-local.c @@ -35,22 +35,26 @@ #include "init.h" #include "restriction-helper.h" #include "datausage-restriction.h" -#include "roaming.h" +#include "telephony.h" #include "storage.h" #include "trace.h" #include "tethering-restriction.h" #define SET_NET_RESTRICTIONS "REPLACE INTO restrictions " \ "(binpath, rcv_limit, send_limit, iftype, rst_state, "\ - " quota_id, roaming) " \ - "VALUES (?, ?, ?, ?, ?, ?, ?)" + " quota_id, roaming, ifname) " \ + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)" #define GET_NET_RESTRICTION "SELECT rcv_limit, send_limit, " \ - " rst_state, quota_id FROM restrictions " \ - "WHERE binpath = ? AND iftype = ?" + " rst_state, roaming, quota_id FROM restrictions " \ + "WHERE binpath = ? AND iftype = ? AND ifname = ?" +#define GET_NET_RESTRICTION_BY_QUOTA "SELECT rcv_limit, send_limit, " \ + " rst_state, roaming, ifname FROM restrictions " \ + "WHERE binpath = ? AND iftype = ? AND quota_id = ?" + #define RESET_RESTRICTIONS "DELETE FROM restrictions " \ - "WHERE binpath=? AND iftype=?" + "WHERE binpath=? AND iftype=? AND ifname = ? AND quota_id = ?" static sqlite3_stmt *update_rst_stm; static sqlite3_stmt *reset_rst_stm; @@ -73,14 +77,23 @@ handle_error: } static resourced_ret_c reset_restriction_db(const char *app_id, - const resourced_iface_type iftype) + const resourced_iface_type iftype, + const char *ifname, + const int quota_id) { resourced_ret_c error_code = init_reset_rst(); ret_value_if(error_code != RESOURCED_ERROR_NONE, error_code); + _D("app_id %s",app_id); + _D("iftype %d", iftype); + _D("ifname %s", ifname); + _D("quota_id %d", quota_id); + DB_ACTION(sqlite3_bind_text(reset_rst_stm, 1, app_id, -1, SQLITE_TRANSIENT)); DB_ACTION(sqlite3_bind_int(reset_rst_stm, 2, iftype)); + DB_ACTION(sqlite3_bind_text(reset_rst_stm, 3, ifname, -1, SQLITE_TRANSIENT)); + DB_ACTION(sqlite3_bind_int(reset_rst_stm, 4, quota_id)); if (sqlite3_step(reset_rst_stm) != SQLITE_DONE) error_code = RESOURCED_ERROR_DB_FAILED; @@ -116,14 +129,10 @@ resourced_ret_c update_restriction_db( const int rcv_limit, const int snd_limit, const resourced_restriction_state rst_state, const int quota_id, - const resourced_roaming_type roaming) + const resourced_roaming_type roaming, + const char *ifname) { - resourced_ret_c error_code = RESOURCED_ERROR_NONE; - - if (rst_state == RESOURCED_RESTRICTION_REMOVED) - return reset_restriction_db(app_id, iftype); - - error_code = init_update_rest_stmt(); + resourced_ret_c error_code = init_update_rest_stmt(); ret_value_if(error_code != RESOURCED_ERROR_NONE, error_code); DB_ACTION(sqlite3_bind_text(update_rst_stm, 1, app_id, -1, SQLITE_TRANSIENT)); @@ -133,6 +142,7 @@ resourced_ret_c update_restriction_db( DB_ACTION(sqlite3_bind_int(update_rst_stm, 5, rst_state)); DB_ACTION(sqlite3_bind_int(update_rst_stm, 6, quota_id)); DB_ACTION(sqlite3_bind_int(update_rst_stm, 7, roaming)); + DB_ACTION(sqlite3_bind_text(update_rst_stm, 8, ifname, -1, SQLITE_TRANSIENT)); if (sqlite3_step(update_rst_stm) != SQLITE_DONE) error_code = RESOURCED_ERROR_DB_FAILED; @@ -147,14 +157,25 @@ handle_error: return error_code; } +/** + * Populate restriction info + * @param app_id mandatory argument + * @param iftype mandatory + * @param rst - restriction to fill, + * if user specified ifname in it + * select will be by ifname + * vice versa, if quota_id was specified we are looking ifname + * in this case user should release ifname if it's no more needed. + * */ resourced_ret_c get_restriction_info(const char *app_id, - const resourced_iface_type iftype, + const resourced_iface_type iftype, resourced_restriction_info *rst) { int rc; resourced_ret_c error_code = RESOURCED_ERROR_NONE; - int quota_id = 0; - sqlite3_stmt *stm = NULL; + static sqlite3_stmt *stm_ifname; + static sqlite3_stmt *stm_quota; + sqlite3_stmt *stm = 0; ret_value_msg_if(rst == NULL, RESOURCED_ERROR_INVALID_PARAMETER, "Please provide valid restriction argument!"); @@ -162,13 +183,32 @@ resourced_ret_c get_restriction_info(const char *app_id, ret_value_msg_if(app_id == NULL, RESOURCED_ERROR_INVALID_PARAMETER, "Please provide valid app_id argument!"); - _SD("%s, %d", app_id, iftype); + _SD("app_id: %s, iftype: %d, ifname: %s, quota_id: %d ", + app_id, iftype, rst->ifname, rst->quota_id); - DB_ACTION(sqlite3_prepare_v2 - (resourced_get_database(), GET_NET_RESTRICTION, -1, &stm, NULL)); + if (rst->ifname && strlen(rst->ifname)) { + if (stm_ifname == NULL) { /* lazy initialization of stm */ + DB_ACTION(sqlite3_prepare_v2( + resourced_get_database(), GET_NET_RESTRICTION, -1, + &stm_ifname, NULL)); + } + stm = stm_ifname; + } else if (rst->quota_id) { + if (stm_quota == NULL) { /* lazy initialization of stm_quota */ + DB_ACTION(sqlite3_prepare_v2( + resourced_get_database(), GET_NET_RESTRICTION_BY_QUOTA, + -1, &stm_quota, NULL)); + } + stm = stm_quota; + } else + return RESOURCED_ERROR_INVALID_PARAMETER; DB_ACTION(sqlite3_bind_text(stm, 1, app_id, -1, SQLITE_TRANSIENT)); DB_ACTION(sqlite3_bind_int(stm, 2, iftype)); + if (rst->ifname && strlen(rst->ifname)) + DB_ACTION(sqlite3_bind_text(stm, 3, rst->ifname, -1, SQLITE_TRANSIENT)); + else if (rst->quota_id) + DB_ACTION(sqlite3_bind_int(stm, 3, rst->quota_id)); do { rc = sqlite3_step(stm); @@ -177,7 +217,12 @@ resourced_ret_c get_restriction_info(const char *app_id, rst->rcv_limit = sqlite3_column_int(stm, 0); rst->send_limit = sqlite3_column_int64(stm, 1); rst->rst_state = sqlite3_column_int(stm, 2); - rst->quota_id = sqlite3_column_int(stm, 3); + rst->roaming = sqlite3_column_int(stm, 3); + if (rst->ifname && strlen(rst->ifname)) + rst->quota_id = sqlite3_column_int(stm, 4); + else if (rst->quota_id) + rst->ifname = strdup((char *)sqlite3_column_text( + stm, 4)); break; case SQLITE_DONE: @@ -188,19 +233,27 @@ resourced_ret_c get_restriction_info(const char *app_id, goto handle_error; } } while (rc == SQLITE_ROW); + sqlite3_reset(stm); - _D("%d", quota_id); + _D("quota_id: %d, if_name: %s", rst->quota_id, rst->ifname); + + return RESOURCED_ERROR_NONE; handle_error: - if (stm) - sqlite3_finalize(stm); + if (stm == stm_ifname) { + sqlite3_finalize(stm_ifname); + stm_ifname = 0; + } else if (stm == stm_quota) { + sqlite3_finalize(stm_quota); + stm_quota = 0; + } if (error_code == RESOURCED_ERROR_DB_FAILED) - _E("Failed to get network restriction's quota id: %s\n", + _E("Failed to fill network restriction's: %s\n", sqlite3_errmsg(resourced_get_database())); - return quota_id; + return error_code; } static bool check_roaming(const resourced_net_restrictions *rst) @@ -209,7 +262,7 @@ static bool check_roaming(const resourced_net_restrictions *rst) ret_value_msg_if(rst == NULL, false, "Invalid net_restriction pointer, please provide valid argument"); - roaming = get_roaming(); + roaming = get_current_roaming(); _D("roaming %d rst->roaming %d", roaming, rst->roaming); if (roaming == RESOURCED_ROAMING_UNKNOWN || rst->roaming == RESOURCED_ROAMING_UNKNOWN) { @@ -232,7 +285,8 @@ static void process_net_block_state(const enum resourced_ret_c process_kernel_restriction( const u_int32_t classid, const resourced_net_restrictions *rst, - const enum traffic_restriction_type rst_type) + const enum traffic_restriction_type rst_type, + const int quota_id) { int ret = RESOURCED_ERROR_NONE; @@ -249,17 +303,23 @@ resourced_ret_c process_kernel_restriction( /* TODO check, and think how to implement it * in unified way, maybe also block FORWARD chain in * send_net_restriction */ - if (classid == RESOURCED_ALL_APP_CLASSID || - classid == RESOURCED_TETHERING_APP_CLASSID) + if ((classid == RESOURCED_ALL_APP_CLASSID || + classid == RESOURCED_TETHERING_APP_CLASSID) && + /* apply it now if we'll block now in case of send_limit + * rcv_limit 0, or when will block noti come */ + ((rst_type == RST_UNSET || rst_type == RST_EXCLUDE) || + (rst_type == RST_SET && (!rst->send_limit || !rst->rcv_limit)))) ret = apply_tethering_restriction(rst_type); + if (classid != RESOURCED_TETHERING_APP_CLASSID && ret == RESOURCED_ERROR_NONE) - ret = send_net_restriction(rst_type, classid, + ret = send_net_restriction(rst_type, classid, quota_id, rst->iftype, rst->send_limit, rst->rcv_limit, rst->snd_warning_limit, - rst->rcv_warning_limit); + rst->rcv_warning_limit, + rst->ifname); ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL, "Restriction, type %d falied, return code %d\n", rst_type, ret); @@ -270,7 +330,8 @@ resourced_ret_c process_kernel_restriction( resourced_ret_c proc_keep_restriction( const char *app_id, const int quota_id, const resourced_net_restrictions *rst, - const enum traffic_restriction_type rst_type) + const enum traffic_restriction_type rst_type, + bool skip_kernel_op) { u_int32_t app_classid = 0; resourced_iface_type store_iftype; @@ -280,41 +341,84 @@ resourced_ret_c proc_keep_restriction( RESOURCED_ERROR_INVALID_PARAMETER, "Invalid restriction arguments\n"); - app_classid = get_classid_by_app_id(app_id, rst_type != RST_UNSET); - ret = process_kernel_restriction(app_classid, rst, rst_type); - ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, - "Can't keep restriction."); + if (rst->rs_type == RESOURCED_STATE_BACKGROUND) + app_classid = RESOURCED_BACKGROUND_APP_CLASSID; + else + app_classid = get_classid_by_app_id(app_id, rst_type != RST_UNSET); + if (!skip_kernel_op) { + ret = process_kernel_restriction(app_classid, rst, rst_type, quota_id); + ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, + "Can't keep restriction."); + } store_iftype = get_store_iftype(app_classid, rst->iftype); rst_state = convert_to_restriction_state(rst_type); - _SD("restriction: app_id %s, iftype %d, state %d, type %d\n", app_id, - store_iftype, rst_state, rst_type); + _SD("restriction: app_id %s, classid %d, iftype %d, state %d, type %d, "\ + "imsi %s, rs_type %d\n", app_id, app_classid, + store_iftype, rst_state, rst_type, rst->ifname, rst->rs_type); if (!strcmp(app_id, RESOURCED_ALL_APP) && rst->iftype == RESOURCED_IFACE_ALL) process_net_block_state(rst_type); - return update_restriction_db(app_id, store_iftype, - rst->rcv_limit, rst->send_limit, - rst_state, quota_id, rst->roaming); + /* in case of SET/EXCLUDE just update state in db, otherwise remove fro + * db */ + if (rst_type == RST_UNSET) + ret = reset_restriction_db(app_id, store_iftype, rst->ifname, + quota_id); + else + ret = update_restriction_db(app_id, store_iftype, + rst->rcv_limit, rst->send_limit, + rst_state, quota_id, rst->roaming, + rst->ifname); + + return ret; } resourced_ret_c remove_restriction_local(const char *app_id, - const resourced_iface_type iftype) + const resourced_iface_type iftype, + const int quota_id, + const char *imsi_hash, + const resourced_state_t ground) { - resourced_net_restrictions rst = { 0 }; + resourced_net_restrictions rst = { .iftype = iftype }; + resourced_restriction_info rst_info = { .iftype = iftype, .quota_id = quota_id }; + + bool skip_kernel_op = check_event_in_current_modem(imsi_hash, iftype); + /* getting ifname by iftype form none persistent + * ifaces list is not so good idea, + * for example, user could delete applied quota, + * right after reboot, when ifnames is not yet initialized */ + resourced_ret_c ret = get_restriction_info(app_id, iftype, &rst_info); + if (ret != RESOURCED_ERROR_NONE) { + _D("Can't get restriction info: app_id %s, iftype %d, quota_id %d", + app_id, iftype, quota_id); + goto release_ifname; + } + rst.ifname = (char *)rst_info.ifname; + rst.rs_type = ground; + ret = proc_keep_restriction(app_id, quota_id, &rst, + RST_UNSET, skip_kernel_op); + if (ret != RESOURCED_ERROR_NONE) { + _D("Can't keep restriction"); + } - rst.iftype = iftype; - return proc_keep_restriction(app_id, NONE_QUOTA_ID, &rst, - RST_UNSET); +release_ifname: + if(rst_info.ifname) + free((char *)rst_info.ifname); + return ret; } resourced_ret_c exclude_restriction_local(const char *app_id, const int quota_id, - const resourced_iface_type iftype) + const resourced_iface_type iftype, + const char *imsi_hash) { resourced_net_restrictions rst = { 0 }; + bool skip_kernel_op = check_event_in_current_modem(imsi_hash, iftype); rst.iftype = iftype; - return proc_keep_restriction(app_id, quota_id, &rst, RST_EXCLUDE); + rst.ifname = get_iftype_name(rst.iftype); + return proc_keep_restriction(app_id, quota_id, &rst, RST_EXCLUDE, + skip_kernel_op); } diff --git a/src/network/restriction.c b/src/network/restriction.c index ffd0ff3e..18995c6b 100644 --- a/src/network/restriction.c +++ b/src/network/restriction.c @@ -25,9 +25,7 @@ #include <sqlite3.h> #include <resourced.h> #include <data_usage.h> -#include <rd-network.h> -#include "cgroup.h" /*get_classid_by_pkg_name */ #include "const.h" #include "database.h" #include "datausage-restriction.h" diff --git a/src/network/roaming.c b/src/network/roaming.c deleted file mode 100644 index 6152200f..00000000 --- a/src/network/roaming.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * resourced - * - * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * - * @file roaming.c - * - * @desc Roaming persistent object. Due roaming changes not so often we can keep it in - * our memory and handle roaming changes. - * In this file we keep roaming state in global variable and change it in callback. - */ - -#include <glib.h> -#include <telephony_network.h> - -#include "roaming.h" -#include "trace.h" -#include "macro.h" - -static resourced_roaming_type roaming_state; - -/* for avoiding dependency in this file */ - -static GSList *roaming_callbacks; - -static void invoke_roaming_callbacks(void) -{ - GSList *func_iter = NULL; - gslist_for_each_item(func_iter, roaming_callbacks) { - if (func_iter && func_iter->data) - ((roaming_cb)func_iter->data)(); - } -} - -void regist_roaming_cb(roaming_cb cb) -{ - roaming_callbacks = g_slist_prepend(roaming_callbacks, cb); -} - -static void on_roaming_change(bool new_roaming, - void UNUSED *user_data) -{ - _D("Roaming is changed %d", (int)new_roaming); - roaming_state = new_roaming ? RESOURCED_ROAMING_ENABLE : RESOURCED_ROAMING_DISABLE; - invoke_roaming_callbacks(); -} - -/** - * @brief Get initial value for roaming and sets callback for handling roaming change - */ -static void init_roaming_state(void) -{ - bool roaming = false; - - if (network_info_set_roaming_state_changed_cb(on_roaming_change, - NULL) != NETWORK_INFO_ERROR_NONE) - _E("Can not register callback for handle roaming changes."); - - if (network_info_is_roaming(&roaming) != NETWORK_INFO_ERROR_NONE) - _E("Failed to get initial roaming state!"); - - roaming_state = roaming ? - RESOURCED_ROAMING_ENABLE : RESOURCED_ROAMING_DISABLE; -} - -resourced_roaming_type get_roaming(void) -{ - execute_once { - init_roaming_state(); - } - return roaming_state; -} - diff --git a/src/network/specific-trace.c b/src/network/specific-trace.c index f1d54d1c..1fd2c0e3 100644 --- a/src/network/specific-trace.c +++ b/src/network/specific-trace.c @@ -46,12 +46,12 @@ gboolean print_appstat(gpointer key, gpointer value, return TRUE; /*stop printing*/ } - _SD("appid %s, pid %d, rcv %u, snd %u, classid %u, iftype %d, ifname %s," \ - " is_roaming %d", - appstat->application_id, appstat->pid, appstat->rcv_count, + _SD("appid %s, rcv %u, snd %u, classid %u, iftype %d, ifname %s," \ + " is_roaming %d, ground %d", + appstat->application_id, appstat->rcv_count, appstat->snd_count, (u_int32_t)composite_key->classid, composite_key->iftype, composite_key->ifname, - appstat->is_roaming); + appstat->is_roaming, appstat->ground); return FALSE; } diff --git a/src/network/storage.c b/src/network/storage.c index f0c67da9..c025fe4f 100644 --- a/src/network/storage.c +++ b/src/network/storage.c @@ -38,14 +38,15 @@ #include <unistd.h> #include "const.h" -#include "iface.h" #include "database.h" #include "datausage-quota-processing.h" +#include "db-guard.h" +#include "iface.h" #include "macro.h" -#include "protocol-info.h" -#include "storage.h" #include "specific-trace.h" +#include "storage.h" #include "trace.h" +#include "telephony.h" static sqlite3_stmt *update_statistics_query; static sqlite3_stmt *update_iface_query; @@ -54,13 +55,15 @@ enum { read_until_null = -1 }; static void handle_on_iface(const int ifindex, resourced_option_state state) { - _D("Handling network interface %d, %d", ifindex, state); - + resourced_iface_type iftype; if (!update_iface_query) { _E("Uninitialized statement"); return; } - sqlite3_bind_int(update_iface_query, 1, get_iftype(ifindex)); + iftype = get_iftype(ifindex); + _D("Handling network interface ifindex:%d, state: %d, iftype: %d", + ifindex, state, iftype); + sqlite3_bind_int(update_iface_query, 1, iftype); sqlite3_bind_int(update_iface_query, 2, state); if (sqlite3_step(update_iface_query) != SQLITE_DONE) @@ -90,8 +93,9 @@ static int init_update_statistics_query(sqlite3 *db) rc = sqlite3_prepare_v2(db, "insert into statistics " \ "(binpath, received, sent, time_stamp, " \ - "iftype, is_roaming, hw_net_protocol_type, ifname) " \ - "values (?, ?, ?, ?, ?, ?, ?, ?)", + "iftype, is_roaming, hw_net_protocol_type, " \ + "ifname, imsi, ground) " \ + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", read_until_null, &update_statistics_query, NULL); if (rc != SQLITE_OK) { @@ -128,7 +132,7 @@ static gboolean store_application_stat(gpointer key, gpointer value, struct classid_iftype_key *stat_key = (struct classid_iftype_key *)key; time_t *last_touch_time = (time_t *)userdata; resourced_hw_net_protocol_type hw_net_protocol_type = - get_hw_net_protocol_type(stat_key->iftype); + get_current_protocol(stat_key->iftype); if (!update_statistics_query) { _E("Uninitialized statement"); @@ -174,6 +178,18 @@ static gboolean store_application_stat(gpointer key, gpointer value, _SE("Can not bind ifname: %s", stat_key->ifname); return FALSE; } + if (sqlite3_bind_text(update_statistics_query, 9, + get_imsi_hash(stat_key->imsi), read_until_null, + SQLITE_STATIC) != SQLITE_OK) { + _SE("Can not bind imsi: %s", stat_key->imsi); + return FALSE; + } + if (sqlite3_bind_int(update_statistics_query, 10, + (int)stat->ground) != SQLITE_OK) { + _E("Can not bind applicaton background type: %d", + (int)stat->ground); + return FALSE; + } /*we want to reuse tree*/ stat->rcv_count = 0; @@ -185,37 +201,35 @@ static gboolean store_application_stat(gpointer key, gpointer value, return FALSE; } -int store_result(struct application_stat_tree *stats, int flush_period) +resourced_ret_c store_result(struct application_stat_tree *stats) { time_t current_time; + pthread_rwlock_rdlock(&stats->guard); + WALK_TREE(stats->tree, print_appstat); + pthread_rwlock_unlock(&stats->guard); + + if (init_update_statistics_query(resourced_get_database()) != SQLITE_OK) { + _D("Failed to initialize data usage quota statements: %s\n", + sqlite3_errmsg(resourced_get_database())); + return RESOURCED_ERROR_FAIL; /* Do not iterate and free results */ + } + time(¤t_time); + stats->last_touch_time = current_time; + + /* it's reader only, we don't modify tree, don't reduce it, + * due we want to reuse it in next iteration */ + pthread_rwlock_rdlock(&stats->guard); + g_tree_foreach((GTree *) stats->tree, + store_application_stat, + &stats->last_touch_time); + + pthread_rwlock_unlock(&stats->guard); + flush_quota_table(); + change_db_entries_num_num(g_tree_nnodes((GTree *)stats->tree)); - if (current_time - stats->last_touch_time >= flush_period) { - - pthread_rwlock_rdlock(&stats->guard); - WALK_TREE(stats->tree, print_appstat); - pthread_rwlock_unlock(&stats->guard); - - if (init_update_statistics_query(resourced_get_database()) != SQLITE_OK) { - _D("Failed to initialize data usage quota statements: %s\n", - sqlite3_errmsg(resourced_get_database())); - return 0; /* Do not iterate and free results */ - } - - /* it's reader only, we don't modify tree, don't reduce it, - * due we want to reuse it in next iteration */ - pthread_rwlock_rdlock(&stats->guard); - g_tree_foreach((GTree *) stats->tree, - store_application_stat, - &stats->last_touch_time); - pthread_rwlock_unlock(&stats->guard); - flush_quota_table(); - time(¤t_time); - stats->last_touch_time = current_time; - return 1; - } - return 0; + return RESOURCED_ERROR_NONE; } void finalize_storage_stm(void) diff --git a/src/network/telephony.c b/src/network/telephony.c new file mode 100644 index 00000000..61cadf0b --- /dev/null +++ b/src/network/telephony.c @@ -0,0 +1,625 @@ +/* + * resourced + * + * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * + * @file telephony.c + * + * @desc Roaming persistent object. Due roaming changes not so often we can keep it in + * our memory and handle roaming changes. + * In this file we keep roaming state in global variable. + */ + +#include <glib.h> +#include <openssl/sha.h> +#include <stdbool.h> +#include <vconf/vconf.h> +#include <vconf/vconf-internal-telephony-keys.h> +#include <TelSim.h> + +#include "config.h" +#include "const.h" +#include "edbus-handler.h" +#include "iface.h" +#include "macro.h" +#include "telephony.h" +#include "trace.h" + +/** + * @brief Definition for the telephony service name. + */ +#define DBUS_TELEPHONY_SERVICE "org.tizen.telephony" + +#define DBUS_TELEPHONY_SERVICE_MANAGER DBUS_TELEPHONY_SERVICE".Manager" +#define DBUS_TELEPHONY_SERVICE_NETWORK DBUS_TELEPHONY_SERVICE".Network" +#define DBUS_TELEPHONY_SIM_INTERFACE DBUS_TELEPHONY_SERVICE".Sim" + + +/** + * @brief Definition for the telephony object path. + */ +#define DBUS_TELEPHONY_DEFAULT_PATH "/org/tizen/telephony" + +#define DBUS_TELEPHONY_GET_MODEMS "GetModems" +#define DBUS_TELEPHONY_PROPERTIES_CHANGED "PropertiesChanged" +#define DBUS_TELEPHONY_ROAMING_STATUS "roaming_status" +#define DBUS_TELEPHONY_SERVICE_TYPE "service_type" +#define DBUS_TELEPHONY_GET "Get" +#define DBUS_TELEPHONY_GET_IMSI "GetIMSI" +#define DBUS_FREEDESKTOP_PROPERTIES "org.freedesktop.DBus.Properties" +#define DBUS_TELEPHONY_STATUS "Status" + +/** + * @brief vconf value for checking active modem + * copied from vconfkey-internal + */ +#define DEFAULT_DATA_SERVICE_SIM1 0 +#define DEFAULT_DATA_SERVICE_SIM2 1 +#define SIM_SLOT_SINGLE 1 +#define IMSI_LENGTH 16 + +struct modem_state { + char *name; + bool roaming; + char *path; + char *imsi; /* International mobile subscriber identity, to identify SIM card */ + /* such model will be if we'll have ability to make 2 connection by 2 sim + * card simultaneously, but for Kiran the "Dual SIM Dual Standby" + * model was chosen, where it's impossible, so keep only pointer to + * current modem, if model will be changed to "Dual SIM Dual Active", + * change get_current_imsi() and + * add into it iteration, also patch + * "[PATCH 2/8] network: add modem section into config" which was + * abondoned in current patch set, could be usefull as well (but not + * confg value in it, due everything could change). */ + /* char *ifname; */ + /* bool active; */ + char *imsi_hash; + resourced_hw_net_protocol_type protocol; +}; + +static struct modem_state *current_modem; +static GSList *modems; /* list of available modems with roaming state */ + +#ifdef DEBUG_ENABLED +#define _D_DBUS _D +#else +#define _D_DBUS(s, args...) ({ do { } while (0); }) +#endif /*DEBUG_ENABLED*/ + +static bool check_current_modem(const char *modem_name, int sim_number) +{ + int digit_pos = 0; + ret_value_msg_if(!modem_name, false, "Invalid argument!"); + + ret_value_msg_if(sim_number >= 10, false, "Unsupported sim number %d", sim_number); + digit_pos = strlen(modem_name); + ret_value_msg_if(!digit_pos, false, "Invalid argument!"); + return modem_name[digit_pos - 1] == '0' + sim_number; +} + +static void default_data_service_change_cb(keynode_t *key, void *data) +{ + int current_sim = vconf_keynode_get_int(key); + GSList *iter; + + _D("default data service has changed: key = %s, value = %d(int)\n", + vconf_keynode_get_name(key), current_sim); + + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + if (!modem->name) + continue; + if (check_current_modem(modem->name, current_sim)) { + current_modem = modem; + break; + } + } +} + +static int get_current_sim(void) +{ + int sim_slot_count = 0; + int current_sim = 0; + ret_value_msg_if(vconf_get_int( + VCONFKEY_TELEPHONY_SIM_SLOT_COUNT, &sim_slot_count) != 0, -1, + "failed to get sim slot count"); + + if(sim_slot_count == SIM_SLOT_SINGLE) { + _D("It's single sim model"); + return -1; + } + + ret_value_msg_if(vconf_get_int( + VCONF_TELEPHONY_DEFAULT_DATA_SERVICE, ¤t_sim) != 0, -1, + "failed to get default data service = %d\n", current_sim); + return current_sim; +} + +static void init_available_modems(void) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter, iter_array; + int i = 0; + int current_sim; + + do { + msg = dbus_method_sync(DBUS_TELEPHONY_SERVICE, + DBUS_TELEPHONY_DEFAULT_PATH, + DBUS_TELEPHONY_SERVICE_MANAGER, + DBUS_TELEPHONY_GET_MODEMS, + NULL, NULL); + if (msg) + break; + _E("Re-try to sync DBUS message, err_count : %d", i); + } while (i++ < RETRY_MAX); + + if (!msg) { + _E("Failed to sync DBUS message."); + return; + } + + dbus_error_init(&err); + + dbus_message_iter_init (msg, &iter); + dbus_message_iter_recurse (&iter, &iter_array); + current_sim = get_current_sim(); + + while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID) { + const char *name; + struct modem_state *state = (struct modem_state *)malloc( + sizeof(struct modem_state)); + memset(state, 0, sizeof(struct modem_state)); + dbus_message_iter_get_basic (&iter_array, &name); + _D("modem name %s", name); + dbus_message_iter_next (&iter_array); + state->name = strdup(name); + state->roaming = false; + modems = g_slist_prepend(modems, state); + if (check_current_modem(state->name, current_sim)) + current_modem = state; + } + + vconf_notify_key_changed(VCONF_TELEPHONY_DEFAULT_DATA_SERVICE, + default_data_service_change_cb, NULL); + + dbus_message_unref(msg); + dbus_error_free(&err); +} + +static void hash_imsi(struct modem_state *modem) +{ + int i; + SHA256_CTX ctx; + SHA256_Init(&ctx); + unsigned char md[SHA256_DIGEST_LENGTH]; + SHA256_Update(&ctx, modem->imsi, strlen(modem->imsi)); + SHA256_Final(md, &ctx); + if (!modem->imsi_hash) { + modem->imsi_hash = (char *)malloc(SHA256_DIGEST_LENGTH * 2 + 1); + ret_msg_if(!modem->imsi_hash, "Can't allocate buffer for imsi_hash!"); + } + _SD("make hash for imsi %s", modem->imsi); + for (i = 0; i < SHA256_DIGEST_LENGTH; i++) + sprintf(modem->imsi_hash + (i * 2), "%02x", md[i]); +} + +static void fill_modem_imsi(struct modem_state *modem) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter; + char tel_path[MAX_PATH_LENGTH]; + char *plmn = NULL; + int plmn_len; + char *msin = NULL; + int msin_len; + int i = 0; + + snprintf(tel_path, sizeof(tel_path), "%s/%s", DBUS_TELEPHONY_DEFAULT_PATH, modem->name); + do { + msg = dbus_method_sync(DBUS_TELEPHONY_SERVICE, + tel_path, + DBUS_TELEPHONY_SIM_INTERFACE, + DBUS_TELEPHONY_GET_IMSI, + NULL, NULL); + if (msg) + break; + _E("Re-try to sync DBUS message, err_count : %d", i); + } while (i++ < RETRY_MAX); + + if (!msg) { + _E("Failed to sync DBUS message."); + return; + } + + dbus_error_init(&err); + + dbus_message_iter_init (msg, &iter); + _D_DBUS("dbus message type %d", dbus_message_iter_get_arg_type(&iter)); + ret_msg_if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING, + "Return for %s isn't variant type as expected", + DBUS_FREEDESKTOP_PROPERTIES); + dbus_message_iter_get_basic (&iter, &plmn); + _D_DBUS("plmn value %d", plmn); + plmn_len = strlen(plmn); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic (&iter, &msin); + _D_DBUS("msin value %d", msin); + msin_len = strlen(msin); + if (!modem->imsi) { /* it's reinit case */ + modem->imsi = malloc(plmn_len + msin_len + 1); + ret_msg_if(!modem->imsi, "Can't allocate string for imsi"); + } + if (msin_len + plmn_len >= IMSI_LENGTH) { + _D("Incorrect length of mobile subscriber identifier + net id"); + return; + } + snprintf(modem->imsi, IMSI_LENGTH, "%s%s", plmn, msin); + hash_imsi(modem); +} + +static void init_modem_imsi(void) +{ + GSList *iter; + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + fill_modem_imsi(modem); + } +} + +static void fill_modem_state(struct modem_state *modem) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter, var; + char tel_path[MAX_PATH_LENGTH]; + int i = 0; + char *params[2] = {DBUS_TELEPHONY_SERVICE_NETWORK, DBUS_TELEPHONY_ROAMING_STATUS}; + + + snprintf(tel_path, sizeof(tel_path), "%s/%s", DBUS_TELEPHONY_DEFAULT_PATH, modem->name); + do { + msg = dbus_method_sync(DBUS_TELEPHONY_SERVICE, + tel_path, + DBUS_FREEDESKTOP_PROPERTIES, + DBUS_TELEPHONY_GET, + "ss", params); + if (msg) + break; + _E("Re-try to sync DBUS message, err_count : %d", i); + } while (i++ < RETRY_MAX); + + if (!msg) { + _E("Failed to sync DBUS message."); + return; + } + + dbus_error_init(&err); + + dbus_message_iter_init (msg, &iter); + _D_DBUS("dbus message type %d", dbus_message_iter_get_arg_type(&iter)); + ret_msg_if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT, + "Return for %s isn't variant type as expected", DBUS_FREEDESKTOP_PROPERTIES); + dbus_message_iter_recurse(&iter, &var); + _D_DBUS("dbus message variant type %d", dbus_message_iter_get_arg_type(&var)); + ret_msg_if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN, + "Return for %s isn't boolean type as expected", DBUS_FREEDESKTOP_PROPERTIES); + + dbus_message_iter_get_basic (&var, &modem->roaming); + _D("modem roaming value %d", modem->roaming); +} + +static void fill_protocol(struct modem_state *modem) +{ + DBusError err; + DBusMessage *msg; + DBusMessageIter iter, var; + char tel_path[MAX_PATH_LENGTH]; + int i = 0; + char *params[2] = {DBUS_TELEPHONY_SERVICE_NETWORK, DBUS_TELEPHONY_SERVICE_TYPE}; + + + snprintf(tel_path, sizeof(tel_path), "%s/%s", DBUS_TELEPHONY_DEFAULT_PATH, modem->name); + do { + msg = dbus_method_sync(DBUS_TELEPHONY_SERVICE, + tel_path, + DBUS_FREEDESKTOP_PROPERTIES, + DBUS_TELEPHONY_GET, + "ss", params); + if (msg) + break; + _E("Re-try to sync DBUS message, err_count : %d", i); + } while (i++ < RETRY_MAX); + + if (!msg) { + _E("Failed to sync DBUS message."); + return; + } + + dbus_error_init(&err); + + dbus_message_iter_init (msg, &iter); + _D_DBUS("dbus message type %d", dbus_message_iter_get_arg_type(&iter)); + ret_msg_if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT, + "Return for %s isn't variant type as expected", DBUS_FREEDESKTOP_PROPERTIES); + dbus_message_iter_recurse(&iter, &var); + _D_DBUS("dbus message variant type %d", dbus_message_iter_get_arg_type(&var)); + ret_msg_if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_INT32, + "Return for %s isn't int type as expected", DBUS_FREEDESKTOP_PROPERTIES); + + dbus_message_iter_get_basic (&var, &modem->protocol); + _D("modem roaming value %d", modem->protocol); +} + +/** + * @brief Get initial value for roaming and sets callback for handling roaming change + */ +static void init_roaming_states(void) +{ + GSList *iter; + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + fill_modem_state(modem); + } +} + +static void init_protocols(void) +{ + GSList *iter; + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + fill_protocol(modem); + } +} + + +/** + * Response format: + * signal sender=:1.273 -> dest=(null destination) serial=546 + * path=/org/tizen/telephony/sprdmodem0; + * interface=org.freedesktop.DBus.Properties; + * member=PropertiesChanged + * string "org.tizen.telephony.Network" + * array [ + * ... + * dict entry( + * string "roaming_status" + * variant boolean false + * ) + * ... + * dict entry( + * string "service_type" + * variant int32 3 + * ) + * ] + * array [ + * ] + **/ +static void edbus_telephony_changed(void *data, DBusMessage *msg) +{ + struct modem_state *modem = (struct modem_state *)data; + char *property; + /* parse msg */ + DBusMessageIter iter, dict, prop, bool_iter; + + _D_DBUS("it's signal by %s path", dbus_message_get_path(msg)); + dbus_message_iter_init (msg, &iter); + dbus_message_iter_next(&iter); + /* call dbus_message_iter_next(&iter) */ + _D_DBUS("dbus message type %d", dbus_message_iter_get_arg_type(&iter)); + + while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) { + dbus_message_iter_recurse(&iter, &dict); + _D_DBUS("dbus message variant type %d", dbus_message_iter_get_arg_type(&dict)); + ret_msg_if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY, + "Return for %s isn't variant type as expected", + modem->path); + + dbus_message_iter_recurse(&dict, &prop); + _D_DBUS("dbus message roaming type %d", dbus_message_iter_get_arg_type(&prop)); + ret_msg_if (dbus_message_iter_get_arg_type(&prop) != DBUS_TYPE_STRING, + "Return for %s isn't boolean type as expected", + modem->path); + + dbus_message_iter_get_basic (&prop, &property); + + if (strcmp(property, DBUS_TELEPHONY_ROAMING_STATUS) == 0) { + dbus_message_iter_next(&prop); /* it's variant here, expand it */ + dbus_message_iter_recurse(&prop, &bool_iter); + ret_msg_if (dbus_message_iter_get_arg_type(&bool_iter) != DBUS_TYPE_BOOLEAN, + "Return for %s isn't variant type as expected", DBUS_FREEDESKTOP_PROPERTIES); + + dbus_message_iter_get_basic (&bool_iter, &modem->roaming); + _D("Roaming state for modem %s has changed", modem->name); + _D("roaming state now is %d", modem->roaming); + } else if (strcmp(property, DBUS_TELEPHONY_SERVICE_TYPE) == 0) { + dbus_message_iter_next(&prop); /* it's variant here, expand it */ + dbus_message_iter_recurse(&prop, &bool_iter); + ret_msg_if (dbus_message_iter_get_arg_type(&bool_iter) != DBUS_TYPE_INT32, + "Return for %s isn't variant type as expected", DBUS_FREEDESKTOP_PROPERTIES); + + dbus_message_iter_get_basic (&bool_iter, &modem->protocol); + _D("Protocol for modem %s has changed", modem->name); + _D("protocol now is %d", modem->protocol); + } else { + _D("Unnecessary property %s", property); + return; + } + dbus_message_iter_next(&iter); + } +} + +static void regist_telephony_callbacks(void) +{ + resourced_ret_c ret; + GSList *iter; + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + modem->path = (char *)malloc(sizeof(DBUS_TELEPHONY_DEFAULT_PATH) + strlen(modem->name) + 2); + sprintf(modem->path, "%s/%s", DBUS_TELEPHONY_DEFAULT_PATH, modem->name); + ret = register_edbus_signal_handler(modem->path, + DBUS_FREEDESKTOP_PROPERTIES, + DBUS_TELEPHONY_PROPERTIES_CHANGED, + edbus_telephony_changed, modem); + if (ret != RESOURCED_ERROR_NONE) { + _E("Could not register edbus path %s", modem->path); + modem->path = NULL; + continue; + } + } +} + +static void edbus_sim_status_changed(void *data, DBusMessage *msg) +{ + struct modem_state *modem = (struct modem_state *)data; + int sim_status = 0; + int arg_type = 0; + /* parse msg */ + DBusMessageIter iter; + + _D("it's signal by %s path", dbus_message_get_path(msg)); + dbus_message_iter_init (msg, &iter); + arg_type = dbus_message_iter_get_arg_type(&iter); + dbus_message_iter_get_basic(&iter, &sim_status); + + _D("sim status type %d, %d", sim_status, arg_type); + if (sim_status == TAPI_SIM_STATUS_SIM_INIT_COMPLETED) { + /* we could request IMSI */ + fill_modem_imsi(modem); + } +} + + +static void regist_sim_status_callbacks(void) +{ + resourced_ret_c ret; + GSList *iter; + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + modem->path = (char *)malloc(sizeof(DBUS_TELEPHONY_DEFAULT_PATH) + strlen(modem->name) + 2); + sprintf(modem->path, "%s/%s", DBUS_TELEPHONY_DEFAULT_PATH, modem->name); + ret = register_edbus_signal_handler(modem->path, + DBUS_TELEPHONY_SIM_INTERFACE, + DBUS_TELEPHONY_STATUS, + edbus_sim_status_changed, modem); + if (ret != RESOURCED_ERROR_NONE) { + _E("Could not register edbus path %s", modem->path); + modem->path = NULL; + continue; + } + } +} + +static void init_telephony(void) +{ + execute_once { + init_available_modems(); + init_roaming_states(); + init_modem_imsi(); + init_protocols(); + regist_telephony_callbacks(); + regist_sim_status_callbacks(); + } +} + +resourced_roaming_type get_current_roaming(void) +{ + init_telephony(); /* one time lazy initialization */ + ret_value_msg_if(!current_modem, RESOURCED_ROAMING_UNKNOWN, + "There is no current modem!"); + + return current_modem->roaming ? RESOURCED_ROAMING_ENABLE: + RESOURCED_ROAMING_DISABLE; +} + +char *get_imsi_hash(char *imsi) +{ + GSList *iter; + + if (!imsi) { + _E("imsi is NULL"); + return NULL; + } + + gslist_for_each_item(iter, modems) { + struct modem_state *modem = (struct modem_state *)iter->data; + if (modem->imsi == NULL) + continue; + if(!strcmp(imsi, modem->imsi)) + return modem->imsi_hash; + } + return NULL; +} + +char *get_current_modem_imsi(void) +{ + init_telephony(); /* one time lazy initialization */ + ret_value_msg_if(current_modem == NULL, NULL, "Current modem isn't " \ + "selected"); + + return current_modem->imsi; +} + +bool check_event_in_current_modem(const char *imsi_hash, + const resourced_iface_type iftype) +{ + char *current_imsi_hash; + if (iftype != RESOURCED_IFACE_DATACALL) + return false; + + current_imsi_hash = get_imsi_hash(get_current_modem_imsi()); + /* if we don't have current_imsi_hash + * do everything as before */ + return (current_imsi_hash && imsi_hash) ? + strcmp(imsi_hash, current_imsi_hash) : false; +} + +static void modem_free(gpointer data) +{ + struct modem_state *modem = (struct modem_state *)data; + if (modem->imsi) + free(modem->imsi); + if (modem->name) + free(modem->name); + if (modem->path) + free(modem->path); +} + +resourced_hw_net_protocol_type get_current_protocol(resourced_iface_type iftype) +{ + if (iftype != RESOURCED_IFACE_DATACALL) + return RESOURCED_PROTOCOL_NONE; + + init_telephony(); + ret_value_msg_if(current_modem == NULL, RESOURCED_PROTOCOL_NONE, + "Current modem isn't selected"); + + return current_modem->protocol; +} + +void finilize_telephony(void) +{ + g_slist_free_full(modems, modem_free); + vconf_ignore_key_changed(VCONF_TELEPHONY_DEFAULT_DATA_SERVICE, + default_data_service_change_cb); + +} + diff --git a/src/network/tethering-restriction.c b/src/network/tethering-restriction.c index ff845022..60abdfe4 100644 --- a/src/network/tethering-restriction.c +++ b/src/network/tethering-restriction.c @@ -34,24 +34,17 @@ resourced_ret_c apply_tethering_restriction( const enum traffic_restriction_type type) { -#ifdef TETHERING_FEATURE - static int tethering_exclude; + _D("apply tethering rule %d", type); switch (type) { case RST_SET: - if (!tethering_exclude) - return fwrite_str(PATH_TO_PROC_IP_FORWARD, "0"); - return RESOURCED_ERROR_NONE; + return fwrite_str(PATH_TO_PROC_IP_FORWARD, "0"); case RST_UNSET: - tethering_exclude = 0; return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1"); case RST_EXCLUDE: - tethering_exclude = 1; return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1"); default: return RESOURCED_ERROR_INVALID_PARAMETER; } -#else return RESOURCED_ERROR_NONE; -#endif /* TETHERING_FEATURE */ } diff --git a/src/network/update.c b/src/network/update.c index e141952a..6e256d6d 100755 --- a/src/network/update.c +++ b/src/network/update.c @@ -30,7 +30,6 @@ #include "const.h" #include "edbus-handler.h" #include "macro.h" -#include "rd-network.h" #include "trace.h" static E_DBus_Signal_Handler *handler; diff --git a/src/proc-stat/CMakeLists.txt b/src/proc-stat/CMakeLists.txt index 573c7548..cdbf5f42 100644 --- a/src/proc-stat/CMakeLists.txt +++ b/src/proc-stat/CMakeLists.txt @@ -3,9 +3,10 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR} ${PROC-STAT_SOURCE_DIR}/include) +SET (REQUIRES_LIST dlog glib-2.0) + INCLUDE(FindPkgConfig) -pkg_check_modules(pkgs_proc_stat REQUIRED dlog - glib-2.0) +pkg_check_modules(pkgs_proc_stat REQUIRED ${REQUIRES_LIST}) FOREACH(flag ${pkgs_proc_stat_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") @@ -26,7 +27,7 @@ SET(SOURCES ADD_LIBRARY(${PROC-STAT} SHARED ${SOURCES}) -TARGET_LINK_LIBRARIES(${PROC-STAT} ${${PROC-STAT}_LDFLAGS}) +TARGET_LINK_LIBRARIES(${PROC-STAT} ${pkgs_proc_stat_LDFLAGS}) SET_TARGET_PROPERTIES(${PROC-STAT} PROPERTIES diff --git a/src/proc-stat/include/proc-main.h b/src/proc-stat/include/proc-main.h index d4afec27..8bf9eb2b 100644 --- a/src/proc-stat/include/proc-main.h +++ b/src/proc-stat/include/proc-main.h @@ -31,17 +31,18 @@ #include "daemon-options.h" #include "resourced.h" #include "const.h" +#include "memcontrol.h" #define PROC_BUF_MAX 64 -#define PROC_NAME_MAX 512 +#define PROC_NAME_MAX 1024 typedef GSList *pid_info_list; enum application_type { - RESOURCED_APP_TYPE_UNKNOWN, - RESOURCED_APP_TYPE_GUI, - RESOURCED_APP_TYPE_SERVICE, - RESOURCED_APP_TYPE_GROUP, + PROC_TYPE_UNKNOWN, + PROC_TYPE_GUI, + PROC_TYPE_SERVICE, + PROC_TYPE_GROUP, }; struct pid_info_t { @@ -52,18 +53,19 @@ struct pid_info_t { struct proc_process_info_t { char appid[MAX_PATH_LENGTH]; char pkgname[MAX_PATH_LENGTH]; + pid_t main_pid; pid_info_list pids; int proc_exclude; int runtime_exclude; int memcg_idx; - int state; + struct memcg_info_t *memcg_info; int type; }; struct proc_status { pid_t pid; char* appid; - struct proc_process_info_t *processinfo; + struct proc_process_info_t *ppi; }; enum proc_exclude_type { @@ -77,14 +79,17 @@ enum { }; enum proc_prelaunch_flags { - PROC_LARGE_HEAP = 0x01u, /* for mark large heap */ + PROC_NONE = 0x00u, + PROC_LARGEMEMORY = 0x01u, /* for mark large memory */ PROC_SIGTERM = 0x02u, /* for make killer kill victim by SIGTERM */ + PROC_WEBAPP = 0x04u, /* for checking webapp */ }; extern int current_lcd_state; +extern GSList *proc_process_list; -void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type); +void proc_add_pid_list(struct proc_process_info_t *ppi, int pid, enum application_type type); int resourced_proc_init(const struct daemon_opts *opts); @@ -97,15 +102,19 @@ int resourced_proc_excluded(const char *app_name); int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_name); +void resourced_proc_dump(int type, const char *path); + struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid); struct pid_info_t *new_pid_info(const pid_t pid, const int type); -void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx); +void proc_set_process_info_memcg(struct proc_process_info_t *ppi, + int memcg_idx, struct memcg_info_t *memcg_info); resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type); struct proc_process_info_t *proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid); +struct proc_process_info_t * proc_create_process_list(const char *appid, const char *pkgid); int proc_remove_process_list(const pid_t pid); void proc_set_apptype(const char *appid, const char *pkgid, int type); - +int proc_get_apptype(const pid_t pid); #endif /*__PROC_MAIN_H__ */ diff --git a/src/proc-stat/include/proc-process.h b/src/proc-stat/include/proc-process.h index f118599d..c0d11a38 100644 --- a/src/proc-stat/include/proc-process.h +++ b/src/proc-stat/include/proc-process.h @@ -65,4 +65,7 @@ pid_t find_pid_from_cmdline(char *cmdline); void proc_set_group(pid_t onwerpid, pid_t childpid); +int proc_set_service_oomscore(const pid_t pid, const int oom_score); + + #endif /*__PROC_PROCESS_H__*/ diff --git a/src/proc-stat/include/proc-usage-stats-helper.h b/src/proc-stat/include/proc-usage-stats-helper.h new file mode 100644 index 00000000..898ee448 --- /dev/null +++ b/src/proc-stat/include/proc-usage-stats-helper.h @@ -0,0 +1,85 @@ +/* + * resourced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * @file proc-usage-stats-helper.h + * @desc process usage stats helper methods + **/ + +#ifndef __RESOURCED_PROC_USAGE_STATS_HELPER_H__ +#define __RESOURCED_PROC_USAGE_STATS_HELPER_H__ + +#include <unistd.h> +#include <fcntl.h> +#include <resourced.h> +#include <E_DBus.h> +#include <pthread.h> +#include <time.h> + +#define INVALID_PROCESS_INFO_FIELD_VALUE -1 + +#define TASK_NAME_SIZE 256 + +/* Process memory usage info struct (original design in runtime-info API) */ +struct process_memory_info_s { + int vsz; /**< Virtual memory size (KiB) */ + int rss; /**< Resident set size (KiB) */ + int pss; /**< Proportional set size (KiB) */ + int shared_clean; /**< Not modified and mapped by other processes (KiB) */ + int shared_dirty; /**< Modified and mapped by other processes (KiB) */ + int private_clean; /**< Not modified and available only to that process (KiB) */ + int private_dirty; /**< Modified and available only to that process (KiB) */ +}; + +struct process_cpu_usage_s { + int utime; /**< Amount of time that this process has spent in user mode */ + int stime; /**< Amount of time that this process has spent in kernel mode */ +}; + + +typedef enum { + RUNTIME_INFO_TASK_MEMORY, /**< Represents memory usage requests */ + RUNTIME_INFO_TASK_CPU /**< Represents cpu usage requests */ +} runtime_info_task_type; + +/* Runtime info task struct. Represents each request received by the runtime-info library */ +struct runtime_info_task { + runtime_info_task_type task_type; /**< Task type */ + int task_size; /**< Size of the process id array */ + int pipe_fds[2]; /**< fds of the read and write end of the pipe */ + char task_name[TASK_NAME_SIZE]; /**< The name assigned to task */ + int *pid_list; /**< Pointer to the process id array in the dbus message */ + void *usage_info_list; /**< Pointer to the memory containing the usage info results */ + DBusMessage *task_msg; /**< Pointer to the dbus message sent by runtime-info. */ +}; + +void proc_get_memory_usage(int pid, struct process_memory_info_s *mem_info); +void proc_get_cpu_usage(int pid, struct process_cpu_usage_s *cpu_usage); +int proc_read_from_usage_struct(void *usage_info_list, int index, int *result, runtime_info_task_type task_type); +void proc_get_task_name(char *task_name, int size, runtime_info_task_type task_type); +void proc_free_runtime_info_task(struct runtime_info_task *rt_task); + +/* TODO + * ** Return different failure values in reply dbus message according to reason + * ** Add some kind of identifier for the process making the method call, + * and use this id in the task file name + * ** Change to thread pool if needed + */ +#endif /* __RESOURCED_PROC_USAGE_STATS_HELPER_H__ */ + diff --git a/src/proc-stat/include/proc-usage-stats.h b/src/proc-stat/include/proc-usage-stats.h new file mode 100644 index 00000000..c1fcb0a2 --- /dev/null +++ b/src/proc-stat/include/proc-usage-stats.h @@ -0,0 +1,33 @@ +/* + * resourced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * @file proc-usage-stats.h + * @desc process usage stats module init + **/ + +#ifndef __RESOURCED_PROC_USAGE_STATS_H__ +#define __RESOURCED_PROC_USAGE_STATS_H__ +#include <resourced.h> + +/* Initialize proc usage stats module by registering it in edbus. */ +resourced_ret_c proc_usage_stats_init(void); + +#endif /* __RESOURCED_PROC_USAGE_STATS_H__ */ + diff --git a/src/proc-stat/proc-main.c b/src/proc-stat/proc-main.c index 2b96fbdf..81e43658 100644 --- a/src/proc-stat/proc-main.c +++ b/src/proc-stat/proc-main.c @@ -36,6 +36,7 @@ #include "trace.h" #include "proc-handler.h" #include "proc-monitor.h" +#include "proc-usage-stats.h" #include "module.h" #include "macro.h" #include "appid-helper.h" @@ -47,6 +48,7 @@ static Ecore_File_Monitor *exclude_list_monitor; static const unsigned int exclude_list_limit = 1024; static int proc_notifd; #define BASE_UGPATH_PREFIX "/usr/ug/bin" +#define LOG_PREFIX "resourced.log" enum proc_state { PROC_STATE_DEFAULT, @@ -54,6 +56,26 @@ enum proc_state { PROC_STATE_BACKGROUND, }; +static char *convert_to_proctype_str(int type) +{ + char *tmp = NULL; + switch (type) { + case PROC_TYPE_GUI: + tmp = "GUI"; + break; + case PROC_TYPE_SERVICE: + tmp = "SERVICE"; + break; + case PROC_TYPE_GROUP: + tmp = "GROUP"; + break; + default: + tmp = "NONE"; + break; + } + return tmp; +} + /* * @brief pid_info_list is only for pid_info_t */ @@ -86,7 +108,7 @@ static struct pid_info_t *find_pid_info(pid_info_list pids, const pid_t pid) struct pid_info_t pid_to_find = { .pid = pid, /* now it doesn't matter */ - .type = RESOURCED_APP_TYPE_UNKNOWN, + .type = PROC_TYPE_UNKNOWN, }; GSList *found = NULL; @@ -100,24 +122,24 @@ static struct pid_info_t *find_pid_info(pid_info_list pids, const pid_t pid) return NULL; } -void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type) +void proc_add_pid_list(struct proc_process_info_t *ppi, int pid, enum application_type type) { struct pid_info_t pid_to_find = { .pid = pid, /* now it doesn't matter */ - .type = RESOURCED_APP_TYPE_UNKNOWN, + .type = PROC_TYPE_UNKNOWN, }; GSList *found = NULL; - if (process_info->pids) - found = g_slist_find_custom((GSList *)process_info->pids, + if (ppi->pids) + found = g_slist_find_custom((GSList *)ppi->pids, &pid_to_find, compare_pid); if (found) return; pthread_mutex_lock(&proc_mutex); - process_info->pids = g_slist_prepend(process_info->pids, new_pid_info(pid, type)); + ppi->pids = g_slist_prepend(ppi->pids, new_pid_info(pid, type)); pthread_mutex_unlock(&proc_mutex); } @@ -149,48 +171,13 @@ static resourced_ret_c proc_check_ug(pid_t pid) return RESOURCED_ERROR_NONE; } -static void proc_set_service_oomscore(const pid_t pid, const int state) -{ - int oom_score = OOMADJ_SERVICE_DEFAULT; - switch(state) { - case PROC_STATE_DEFAULT: - oom_score = OOMADJ_SERVICE_DEFAULT; - break; - case PROC_STATE_FOREGROUND: - oom_score = OOMADJ_SERVICE_FOREGRD; - break; - case PROC_STATE_BACKGROUND: - oom_score = OOMADJ_SERVICE_BACKGRD; - break; - } - proc_set_oom_score_adj(pid, oom_score); -} - -static pid_t get_service_pid(struct proc_process_info_t *info_t) -{ - GSList *iter = NULL; - - if (!info_t) { - _D("Can't find process_info"); - return RESOURCED_ERROR_FAIL; - } - - gslist_for_each_item(iter, info_t->pids) { - struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data); - - if (pid_info->type == RESOURCED_APP_TYPE_SERVICE) { - _D("get_service_pid : pid (%d), type (%d)", pid_info->pid, pid_info->type); - return pid_info->pid; - } - } - return RESOURCED_ERROR_NO_DATA; -} - -void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx) +void proc_set_process_info_memcg(struct proc_process_info_t *ppi, + int memcg_idx, struct memcg_info_t *memcg_info) { - if (!process_info) + if (!ppi) return; - process_info->memcg_idx = memcg_idx; + ppi->memcg_idx = memcg_idx; + ppi->memcg_info = memcg_info; } struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid) @@ -224,47 +211,31 @@ struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid return NULL; } -static resourced_ret_c proc_update_process_state(const pid_t pid, const int state) -{ - struct proc_process_info_t *process_info = NULL; - pid_t service_pid; - process_info = find_process_info(NULL, pid, NULL); - if (!process_info) { - _E("Current pid (%d) didn't have any process list", pid); - return RESOURCED_ERROR_INVALID_PARAMETER; - } - process_info->state = state; - service_pid = get_service_pid(process_info); - if (service_pid) - proc_set_service_oomscore(service_pid, state); - return RESOURCED_ERROR_NONE; -} - resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type) { GSList *iter = NULL; - struct proc_process_info_t *process_info = NULL; - struct pid_info_t *found_pid = NULL; + struct proc_process_info_t *ppi = NULL; + struct pid_info_t *found = NULL; gslist_for_each_item(iter, proc_process_list) { - process_info = (struct proc_process_info_t *)iter->data; - if (!process_info->pids) + ppi = (struct proc_process_info_t *)iter->data; + if (!ppi->pids) continue; - found_pid = find_pid_info(process_info->pids, pid); - if(!found_pid) + found = find_pid_info(ppi->pids, pid); + if(!found) continue; - if(process_info->runtime_exclude) { + if(ppi->runtime_exclude) { if (type == PROC_EXCLUDE) - process_info->runtime_exclude++; + ppi->runtime_exclude++; else - process_info->runtime_exclude--; + ppi->runtime_exclude--; } else - process_info->runtime_exclude = type; + ppi->runtime_exclude = type; _D("found_pid %d, set proc exclude list, type = %d, exclude = %d", - found_pid->pid, type, process_info->runtime_exclude); + found->pid, type, ppi->runtime_exclude); break; } return RESOURCED_ERROR_NONE; @@ -272,111 +243,183 @@ resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type) struct proc_process_info_t * proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid) { - struct proc_process_info_t *process_info; + struct proc_process_info_t *ppi; + int owner_oom = 0; if (!appid) return NULL; - process_info = find_process_info(appid, pid, pkgid); + ppi = find_process_info(appid, pid, pkgid); /* do not add if it already in list */ - if (process_info && find_pid_info(process_info->pids, pid)) - return process_info; + if (ppi && find_pid_info(ppi->pids, pid)) + return ppi; - if (!process_info) { - process_info = malloc(sizeof(struct proc_process_info_t)); - if (!process_info) + if (!ppi) { + ppi = malloc(sizeof(struct proc_process_info_t)); + if (!ppi) return NULL; - memset(process_info, 0, sizeof(struct proc_process_info_t)); - strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1); - process_info->proc_exclude = resourced_proc_excluded(appid); + memset(ppi, 0, sizeof(struct proc_process_info_t)); + strncpy(ppi->appid, appid, MAX_NAME_LENGTH - 1); + ppi->proc_exclude = resourced_proc_excluded(appid); if (pkgid) - strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1); + strncpy(ppi->pkgname, pkgid, MAX_NAME_LENGTH - 1); else - extract_pkgname(process_info->appid, process_info->pkgname, + extract_pkgname(ppi->appid, ppi->pkgname, MAX_NAME_LENGTH); pthread_mutex_lock(&proc_mutex); proc_process_list = g_slist_prepend(proc_process_list, - process_info); + ppi); pthread_mutex_unlock(&proc_mutex); - process_info->state = PROC_STATE_DEFAULT; - } - if (proc_check_ug(pid) == RESOURCED_ERROR_NONFREEZABLE) - process_info->runtime_exclude = PROC_EXCLUDE; - if (type == RESOURCED_APP_TYPE_SERVICE) - proc_set_service_oomscore(pid, process_info->state); + } else if (type == PROC_TYPE_SERVICE && ppi->main_pid > 0) + proc_get_oom_score_adj(ppi->main_pid, &owner_oom); - proc_add_pid_list(process_info, pid, type); - return process_info; + if (proc_check_ug(pid) == RESOURCED_ERROR_NONFREEZABLE) + ppi->runtime_exclude = PROC_EXCLUDE; + if (type == PROC_TYPE_SERVICE) + proc_set_service_oomscore(pid, owner_oom); + + if (type == PROC_TYPE_GUI) { + if (ppi->main_pid && pid != ppi->main_pid){ + if (proc_get_oom_score_adj(pid, &owner_oom) < 0) { + struct pid_info_t *found = NULL; + found = find_pid_info(ppi->pids, ppi->main_pid); + if(found) { + ppi->pids = g_slist_remove(ppi->pids, + found); + free(found); + } + ppi->main_pid = pid; + } + } else + ppi->main_pid = pid; + } + proc_add_pid_list(ppi, pid, type); + return ppi; } struct proc_process_info_t * proc_create_process_list(const char *appid, const char *pkgid) { - struct proc_process_info_t *process_info; + struct proc_process_info_t *ppi; if (!appid) return NULL; - process_info = find_process_info(appid, 0, pkgid); + ppi = find_process_info(appid, 0, pkgid); /* do not add if it already in list */ - if (process_info) - return process_info; + if (ppi) + return ppi; - if (!process_info) { - process_info = malloc(sizeof(struct proc_process_info_t)); - if (!process_info) + if (!ppi) { + ppi = malloc(sizeof(struct proc_process_info_t)); + if (!ppi) return NULL; - memset(process_info, 0, sizeof(struct proc_process_info_t)); - strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1); - process_info->proc_exclude = resourced_proc_excluded(appid); + memset(ppi, 0, sizeof(struct proc_process_info_t)); + strncpy(ppi->appid, appid, MAX_NAME_LENGTH - 1); + ppi->proc_exclude = resourced_proc_excluded(appid); if (pkgid) - strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1); + strncpy(ppi->pkgname, pkgid, MAX_NAME_LENGTH - 1); else - extract_pkgname(process_info->appid, process_info->pkgname, + extract_pkgname(ppi->appid, ppi->pkgname, MAX_NAME_LENGTH); pthread_mutex_lock(&proc_mutex); proc_process_list = g_slist_prepend(proc_process_list, - process_info); + ppi); pthread_mutex_unlock(&proc_mutex); - process_info->state = PROC_STATE_DEFAULT; } - return process_info; + return ppi; } int proc_remove_process_list(const pid_t pid) { GSList *iter = NULL; - struct proc_process_info_t *process_info = NULL; - struct pid_info_t *found_pid = NULL; + struct proc_process_info_t *ppi = NULL; + struct pid_info_t *found = NULL; pthread_mutex_lock(&proc_mutex); gslist_for_each_item(iter, proc_process_list) { - process_info = (struct proc_process_info_t *)iter->data; - if (!process_info->pids) + ppi = (struct proc_process_info_t *)iter->data; + if (!ppi->pids) continue; - found_pid = find_pid_info(process_info->pids, pid); - if(!found_pid) + found = find_pid_info(ppi->pids, pid); + if(!found) continue; - _D("found_pid %d", found_pid->pid); + _D("found_pid %d", found->pid); + if (ppi->main_pid == pid) + ppi->main_pid = 0; /* Introduce function for removing and cleaning */ - process_info->pids = g_slist_remove(process_info->pids, - found_pid); - free(found_pid); - if (!process_info->pids) { - proc_process_list = g_slist_remove( - proc_process_list, - process_info); - free(process_info); - } + ppi->pids = g_slist_remove(ppi->pids, + found); + free(found); break; } pthread_mutex_unlock(&proc_mutex); return 0; } +int proc_delete_process_list(void) +{ + GSList *iter, *next; + GSList *pid_iter = NULL; + struct proc_process_info_t *ppi = NULL; + + pthread_mutex_lock(&proc_mutex); + gslist_for_each_safe(proc_process_list, iter, next, ppi) { + if (ppi->pids) { + gslist_for_each_item(pid_iter, ppi->pids) { + struct pid_info_t *pi = (struct pid_info_t *)(pid_iter->data); + if (pi) { + ppi->pids = g_slist_remove(ppi->pids, pi); + free(pi); + } + } + } + proc_process_list = g_slist_remove(proc_process_list, ppi); + free(ppi); + } + pthread_mutex_unlock(&proc_mutex); + return 0; +} + +static void proc_dump_process_list(FILE* fp) +{ + GSList *iter = NULL; + GSList *iter_pid = NULL; + struct proc_process_info_t *process_info = NULL; + int index = 0 ; + + LOG_DUMP(fp, "[PROCESS LISTS]\n"); + gslist_for_each_item(iter, proc_process_list) { + process_info = (struct proc_process_info_t *)iter->data; + LOG_DUMP(fp, "index : %d, appid : %s, pkgname : %s, main_pid : %d, type : %x\n", + index, process_info->appid, process_info->pkgname, + process_info->main_pid, process_info->type); + if (process_info->pids) { + gslist_for_each_item(iter_pid, process_info->pids) { + struct pid_info_t *pid_info = (struct pid_info_t *)(iter_pid->data); + if (pid_info) { + pid_t pid; + int oom_score_adj; + char appname[PROC_NAME_MAX]; + pid = pid_info->pid; + if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0 || + proc_get_cmdline(pid, appname) < 0) { + process_info->pids = g_slist_remove(process_info->pids, pid_info); + free(pid_info); + continue; + } + LOG_DUMP(fp, "\t type : %s, pid : %d, command line : %s, oom_score : %d\n", + convert_to_proctype_str(pid_info->type), pid, appname, oom_score_adj); + } + } + } + index++; + } +} + static void proc_free_exclude_key(gpointer data) { if (data) @@ -502,18 +545,23 @@ int resourced_proc_init(const struct daemon_opts *opts) { int ret; - proc_notifd = proc_noti_init( ); + proc_notifd = proc_noti_init(); ret = proc_monitor_init(); if (ret) _E("proc_monitor_init failed : %d", ret); + ret = proc_usage_stats_init(); + if (ret) + _E("proc_usage_stats_init failed : %d", ret); + proc_exclude_init(); return ret; } int resourced_proc_exit(const struct daemon_opts *opts) { + proc_delete_process_list(); if (proc_notifd) close(proc_notifd); g_hash_table_destroy(proc_exclude_list); @@ -524,22 +572,32 @@ int resourced_proc_exit(const struct daemon_opts *opts) void proc_set_apptype(const char *appid, const char *pkgid, int type) { - struct proc_process_info_t *process_info = + struct proc_process_info_t *ppi = proc_create_process_list(appid, pkgid); - if (process_info) - process_info->type = type; + if (ppi) + ppi->type = type; +} + +int proc_get_apptype(const pid_t pid) +{ + struct proc_process_info_t *ppi = + find_process_info(NULL, pid, NULL); + + if (ppi) { + _D("get apptype = %d", ppi->type); + return ppi->type; + } else + _D("there is no process info for pid = %d", pid); + return PROC_NONE; } int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_name) { int ret = 0, oom_score_adj = 0; - char pidbuf[32]; - struct proc_status proc_data;; + char pidbuf[MAX_DEC_SIZE(int)]; + struct proc_status proc_data = {0}; if (pid && (proc_get_oom_score_adj(pid, &oom_score_adj) < 0)) { - /* due process with pid is no longer exits - * we need to remove it from - * freezer_process_list */ proc_remove_process_list(pid); _E("Empty pid or process not exists. %d", pid); return RESOURCED_ERROR_FAIL; @@ -552,7 +610,7 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_ proc_data.pid = pid; proc_data.appid = app_name; - proc_data.processinfo = NULL; + proc_data.ppi = NULL; switch (type) { case PROC_CGROUP_SET_FOREGRD: _SD("set foreground : %d", pid); @@ -561,7 +619,8 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_ ret = proc_set_foregrd(pid, oom_score_adj); if (ret != 0) return RESOURCED_ERROR_NO_DATA; - proc_update_process_state(pid, PROC_STATE_FOREGROUND); + proc_data.ppi = find_process_info(app_name, pid, NULL); + proc_data.appid = proc_data.ppi->appid; resourced_notify(RESOURCED_NOTIFIER_APP_FOREGRD, &proc_data); break; case PROC_CGROUP_SET_LAUNCH_REQUEST: @@ -575,7 +634,7 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_ _SD("launch request %s with pkgname", pkg_name); ret = resourced_proc_excluded(app_name); if (!ret) - proc_data.processinfo = proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name); + proc_data.ppi = proc_add_process_list(PROC_TYPE_GUI, pid, app_name, pkg_name); resourced_notify(RESOURCED_NOTIFIER_APP_LAUNCH, &proc_data); _E("available memory = %u", get_available()); break; @@ -587,29 +646,26 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_ _SD("service launch request %s, %d", app_name, pid); if (pkg_name) _SD("launch request %s with pkgname", pkg_name); - proc_add_process_list(RESOURCED_APP_TYPE_SERVICE, pid, app_name, pkg_name); + proc_data.ppi = proc_add_process_list(PROC_TYPE_SERVICE, pid, app_name, pkg_name); if (resourced_proc_excluded(app_name) == RESOURCED_ERROR_NONE) resourced_notify(RESOURCED_NOTIFIER_SERVICE_LAUNCH, &proc_data); break; case PROC_CGROUP_SET_RESUME_REQUEST: _SD("resume request %d", pid); /* init oom_score_value */ - if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED) { - resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &proc_data); - proc_set_oom_score_adj(pid, OOMADJ_INIT); - } - if (!app_name) { _E("need application name!pid = %d", pid); return RESOURCED_ERROR_NO_DATA; } - proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name); - if (ret != RESOURCED_ERROR_NONE) - _D("Failed to add to freezer list: pid %d", pid); - + proc_data.ppi = proc_add_process_list(PROC_TYPE_GUI, pid, app_name, pkg_name); + if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED) { + resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &proc_data); + proc_set_oom_score_adj(pid, OOMADJ_INIT); + } break; case PROC_CGROUP_SET_TERMINATE_REQUEST: + proc_data.ppi = find_process_info(app_name, pid, NULL); resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATE, &proc_data); proc_remove_process_list(pid); break; @@ -626,7 +682,6 @@ int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_ ret = proc_set_backgrd(pid, oom_score_adj); if (ret != 0) break; - proc_update_process_state(pid, PROC_STATE_BACKGROUND); break; case PROC_CGROUP_SET_INACTIVE: ret = proc_set_inactive(pid, oom_score_adj); @@ -673,3 +728,16 @@ int resourced_proc_action(int type, int argnum, char **arg) return resourced_proc_status_change(type, pid, cgroup_name, pkg_name); } +void resourced_proc_dump(int mode, const char *dirpath) +{ + char buf[PROC_BUF_MAX]; + FILE *f = NULL; + if (dirpath) { + snprintf(buf, sizeof(buf), "%s/%s", dirpath, LOG_PREFIX); + f = fopen(buf, "w+"); + } + proc_dump_process_list(f); + modules_dump((void*)f, mode); + if (f) + fclose(f); +} diff --git a/src/proc-stat/proc-monitor.c b/src/proc-stat/proc-monitor.c index d97a6336..a9601f32 100644 --- a/src/proc-stat/proc-monitor.c +++ b/src/proc-stat/proc-monitor.c @@ -39,8 +39,6 @@ #include "lowmem-handler.h" #include "notifier.h" - - #define WATCHDOG_LAUNCHING_PARAM "WatchdogPopupLaunch" #define WATCHDOG_KEY1 "_SYSPOPUP_CONTENT_" #define WATCHDOG_KEY2 "_APP_NAME_" @@ -56,6 +54,9 @@ #define SIGNAL_PROC_GROUP "ProcGroup" #define TIZEN_DEBUG_MODE_FILE "/opt/etc/.debugmode" +#define SIGNAL_DUMP "Dump" +#define SIGNAL_DUMP_START "Start" +#define SIGNAL_DUMP_FINISH "Finish" #define INIT_PID 1 #define INIT_PROC_VAL -1 @@ -235,6 +236,8 @@ static void proc_dbus_prelaunch_signal_handler(void *data, DBusMessage *msg) char *appid; char *pkgid; int flags; + struct proc_status proc_data;; + struct proc_process_info_t *ppi; ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_PRELAUNCH); @@ -256,10 +259,13 @@ static void proc_dbus_prelaunch_signal_handler(void *data, DBusMessage *msg) _D("call proc_dbus_prelaunch_handler: appid = %s, pkgid = %s, flags = %d", appid, pkgid, flags); - if (flags & PROC_LARGE_HEAP) { - proc_set_apptype(appid, pkgid, PROC_LARGE_HEAP); + ppi = proc_create_process_list(appid, pkgid); + ppi->type = flags; + proc_data.appid = appid; + proc_data.ppi = ppi; + resourced_notify(RESOURCED_NOTIFIER_APP_PRELAUNCH, &proc_data); + if (flags & PROC_LARGEMEMORY) lowmem_dynamic_process_killer(DYNAMIC_KILL_LARGEHEAP); - } } static void proc_dbus_sweep_signal_handler(void *data, DBusMessage *msg) @@ -432,7 +438,7 @@ static void proc_dbus_grouping_handler(void *data, DBusMessage *msg) int pid, childpid, ret; if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_GROUP) == 0) { - _D("there is no watchdog result signal"); + _D("there is no process group signal"); return; } @@ -445,10 +451,44 @@ static void proc_dbus_grouping_handler(void *data, DBusMessage *msg) _D("there is no message"); return; } - + _D("received process grouping : owner %d, child %d", pid, childpid); proc_set_group(pid, childpid); } +static void send_dump_signal(char *signal) +{ + pid_t pid = getpid(); + + broadcast_edbus_signal(DUMP_SERVICE_OBJECT_PATH, + DUMP_SERVICE_INTERFACE_NAME, signal, DBUS_TYPE_INT32, &pid); +} + +static void proc_dbus_dump_handler(void *data, DBusMessage *msg) +{ + DBusError err; + dbus_bool_t ret; + char *path; + int mode; + + if (dbus_message_is_signal(msg, DUMP_SERVICE_INTERFACE_NAME, SIGNAL_DUMP) == 0) { + _D("there is no process group signal"); + return; + } + + dbus_error_init(&err); + + ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &mode, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + if (ret == 0) { + _D("there is no message"); + return; + } + _D("received dump request : path %s", path); + send_dump_signal(SIGNAL_DUMP_START); + resourced_proc_dump(mode, path); + send_dump_signal(SIGNAL_DUMP_FINISH); +} + static DBusMessage *edbus_signal_trigger(E_DBus_Object *obj, DBusMessage *msg) { DBusMessage *reply; @@ -520,41 +560,44 @@ static resourced_ret_c proc_dbus_init(void) { register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG_RESULT, - proc_dbus_watchdog_result); + proc_dbus_watchdog_result, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_ACTIVE, - proc_dbus_active_signal_handler); + proc_dbus_active_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_EXCLUDE, - proc_dbus_exclude_signal_handler); + proc_dbus_exclude_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_PRELAUNCH, - proc_dbus_prelaunch_signal_handler); + proc_dbus_prelaunch_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_STATUS, - proc_dbus_proc_signal_handler); + proc_dbus_proc_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_SWEEP, - proc_dbus_sweep_signal_handler); + proc_dbus_sweep_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG, - proc_dbus_watchdog_handler); + proc_dbus_watchdog_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, - SIGNAL_PROC_WATCHDOG, - proc_dbus_grouping_handler); + SIGNAL_PROC_GROUP, + proc_dbus_grouping_handler, NULL); register_edbus_signal_handler(DEVICED_PATH_DISPLAY, - DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_ON, proc_dbus_lcd_on); + DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_ON, proc_dbus_lcd_on, NULL); register_edbus_signal_handler(DEVICED_PATH_DISPLAY, - DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_OFF, proc_dbus_lcd_off); + DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_OFF, proc_dbus_lcd_off, NULL); + + register_edbus_signal_handler(DUMP_SERVICE_OBJECT_PATH, + DUMP_SERVICE_INTERFACE_NAME, SIGNAL_DUMP, proc_dbus_dump_handler, NULL); /* start watchdog check timer for preveting ANR during booting */ watchdog_check_timer = diff --git a/src/proc-stat/proc-process.c b/src/proc-stat/proc-process.c index a299bcfe..bd2445de 100644 --- a/src/proc-stat/proc-process.c +++ b/src/proc-stat/proc-process.c @@ -35,7 +35,6 @@ #include "lowmem-common.h" #include "logging-common.h" #include "macro.h" -#include "swap-common.h" #include "proc-noti.h" #include "notifier.h" @@ -49,116 +48,91 @@ enum proc_background_type { PROC_BACKGROUND_ACTIVE, }; -static int proc_backgrd_manage(int currentpid, int active) +int proc_set_service_oomscore(const pid_t pid, const int oom_score) { - int pid = -1, pgid, ret; + int service_oom; + if (oom_score > 0) + service_oom = oom_score - OOMADJ_SERVICE_GAP; + else + service_oom = OOMADJ_SERVICE_DEFAULT; + return proc_set_oom_score_adj(pid, service_oom); +} + +static int proc_backgrd_manage(int currentpid, int active, int oom_score_adj) +{ + int pid = -1, ret, flag = RESOURCED_NOTIFIER_APP_BACKGRD; struct proc_status proc_data;; - DIR *dp; - struct dirent *dentry; FILE *fp; char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0}; - char appname[PROC_NAME_MAX]; - int count = 0; - int candidatepid[16] = {0,}; - int cur_oom = -1, prev_oom=OOMADJ_BACKGRD_UNLOCKED, select_pid=0; + int cur_oom = -1; static int checkprevpid = 0; - unsigned long swap_args[2] = {0,}; - unsigned long logging_args[3] = {0,}; - - ret = proc_get_cmdline(currentpid, appname); - if (ret == RESOURCED_ERROR_NONE) { - ret = resourced_proc_excluded(appname); - if (!active && !ret) { - proc_data.pid = currentpid; - proc_data.appid = appname; - resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD, &proc_data); - } - if (ret) { - _D("BACKGRD MANAGE : don't manage background application by %d", currentpid); - return RESOURCED_ERROR_NONE; - } - } + GSList *iter; + struct proc_process_info_t *ppi = + find_process_info(NULL, currentpid, NULL); - dp = opendir("/proc"); - if (!dp) { - _E("BACKGRD MANAGE : fail to open /proc"); - return RESOURCED_ERROR_FAIL; + if (!ppi || ppi->proc_exclude) { + _D("BACKGRD MANAGE : don't manage background application by %d", currentpid); + return RESOURCED_ERROR_NONFREEZABLE; } - while ((dentry = readdir(dp)) != NULL) { - - if (!isdigit(dentry->d_name[0])) - continue; - - pid = atoi(dentry->d_name); - pgid = getpgid(pid); - if (!pgid) - continue; - - snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid); - fp = fopen(buf, "r+"); - if (fp == NULL) - continue; - if (fgets(buf, sizeof(buf), fp) == NULL) { - fclose(fp); - continue; - } - cur_oom = atoi(buf); - - logging_args[0] = (unsigned long)pid; - logging_args[1] = (unsigned long)pgid; - logging_args[2] = (unsigned long)cur_oom; - logging_control(LOGGING_INSERT_PROC_LIST, logging_args); - - if (currentpid != pid && currentpid == pgid) { - proc_set_group(currentpid, pid); - fclose(fp); - continue; - } - - if (active || checkprevpid == currentpid) { - fclose(fp); - continue; - } - if (select_pid != pid && select_pid == pgid && count < 16) - { - _D("found candidate child pid = %d, pgid = %d", pid, pgid); - candidatepid[count++] = pid; - fclose(fp); - continue; - } + proc_data.pid = currentpid; + proc_data.appid = ppi->appid; + proc_data.ppi = ppi; + if (active) + flag = RESOURCED_NOTIFIER_APP_BACKGRD_ACTIVE; + resourced_notify(flag, &proc_data); + + if (active) + return RESOURCED_ERROR_NONE; + + if (checkprevpid != currentpid) { + gslist_for_each_item(iter, proc_process_list) { + struct proc_process_info_t *spi = (struct proc_process_info_t *)iter->data; + if (!spi->pids || !spi->main_pid) + continue; - swap_args[0] = (unsigned long)pid; - if (cur_oom > OOMADJ_BACKGRD_UNLOCKED && cur_oom > prev_oom - && !swap_status(SWAP_CHECK_PID, swap_args)) { - count = 0; - candidatepid[count++] = pid; - select_pid = pid; - prev_oom = cur_oom; - } + pid = spi->main_pid; + snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid); + fp = fopen(buf, "r+"); + if (fp == NULL) { + spi->main_pid = 0; + continue; + } + if (fgets(buf, sizeof(buf), fp) == NULL) { + fclose(fp); + spi->main_pid = 0; + continue; + } + cur_oom = atoi(buf); - if (cur_oom >= OOMADJ_APP_MAX) { + if (cur_oom >= OOMADJ_APP_MAX) { + fclose(fp); + continue; + } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) { + _D("BACKGRD : process %d set score %d (before %d)", + pid, cur_oom+OOMADJ_APP_INCREASE, cur_oom); + fprintf(fp, "%d", cur_oom+OOMADJ_APP_INCREASE); + } fclose(fp); - continue; - } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) { - _D("BACKGRD : process %d set score %d (before %d)", - pid, cur_oom+OOMADJ_APP_INCREASE, cur_oom); - fprintf(fp, "%d", cur_oom+OOMADJ_APP_INCREASE); } - fclose(fp); } - if (select_pid) { - _D("found candidate pid = %d, count = %d", candidatepid, count); - swap_args[0] = (unsigned long)candidatepid; - swap_args[1] = (unsigned long)count; - resourced_notify(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_args); + gslist_for_each_item(iter, ppi->pids) { + struct pid_info_t *pi = (struct pid_info_t *)(iter->data); + if (pi) { + if (pi->type == PROC_TYPE_SERVICE) + ret = proc_set_service_oomscore(pi->pid, oom_score_adj); + else if (pi->type == PROC_TYPE_GUI && ppi->main_pid != pi->pid) + continue; + else + ret = proc_set_oom_score_adj(pi->pid, oom_score_adj); + if (ret < 0) { + ppi->pids = g_slist_remove(ppi->pids, pi); + free(pi); + } + } } checkprevpid = currentpid; - closedir(dp); - - logging_control(LOGGING_UPDATE_PROC_INFO, NULL); - return RESOURCED_ERROR_NONE; } @@ -166,17 +140,22 @@ static int proc_foregrd_manage(int pid, int oom_score_adj) { int ret = 0; GSList *iter; - struct proc_process_info_t *process_info = + struct proc_process_info_t *ppi = find_process_info(NULL, pid, NULL); - if (!process_info) { - ret = proc_set_oom_score_adj(pid, oom_score_adj); - return ret; + if (!ppi) { + proc_set_oom_score_adj(pid, oom_score_adj); + return RESOURCED_ERROR_NO_DATA; } - gslist_for_each_item(iter, process_info->pids) { - struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data); - ret = proc_set_oom_score_adj(pid_info->pid, oom_score_adj); + gslist_for_each_item(iter, ppi->pids) { + struct pid_info_t *pi = (struct pid_info_t *)(iter->data); + if (pi->type == PROC_TYPE_SERVICE) + ret = proc_set_service_oomscore(pi->pid, oom_score_adj); + else if (pi->type == PROC_TYPE_GUI && ppi->main_pid != pi->pid) + continue; + else + ret = proc_set_oom_score_adj(pi->pid, oom_score_adj); } return ret; } @@ -219,8 +198,6 @@ int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid) { pid_t pid = -1; int count=0, ret; - DIR *dp; - struct dirent *dentry; FILE *fp; gint *piddata; char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0}; @@ -228,11 +205,9 @@ int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid) int cur_oom = -1; int select_sweep_limit; - dp = opendir("/proc"); - if (!dp) { - _E("BACKGRD MANAGE : fail to open /proc"); - return RESOURCED_ERROR_FAIL; - } + GSList *iter; + struct proc_process_info_t *ppi; + if (proc_sweep_timer) ecore_timer_del(proc_sweep_timer); if (proc_sweep_list) @@ -244,13 +219,12 @@ int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid) else select_sweep_limit = OOMADJ_BACKGRD_LOCKED; - while ((dentry = readdir(dp)) != NULL) { - if (!isdigit(dentry->d_name[0])) + gslist_for_each_item(iter, proc_process_list) { + ppi = (struct proc_process_info_t *)iter->data; + if (!ppi->pids || !ppi->main_pid || callpid == ppi->main_pid) continue; - pid = atoi(dentry->d_name); - if (pid == callpid) - continue; + pid = ppi->main_pid; snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid); fp = fopen(buf, "r+"); @@ -282,7 +256,6 @@ int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid) proc_sweep_timer = ecore_timer_add(PROC_SWEEP_TIMER, proc_check_sweep_cb, (void *)proc_sweep_list); } - closedir(dp); return count; } @@ -384,7 +357,7 @@ int proc_set_foregrd(pid_t pid, int oom_score_adj) break; default: if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) { - ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED); + ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED); } else { ret = -1; } @@ -405,12 +378,10 @@ int proc_set_backgrd(int pid, int oom_score_adj) ret = -1; break; case OOMADJ_FOREGRD_LOCKED: - proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE); - ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED); + ret = proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE, OOMADJ_BACKGRD_LOCKED); break; case OOMADJ_FOREGRD_UNLOCKED: - proc_backgrd_manage(pid, PROC_BACKGROUND_INACTIVE); - ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED); + ret = proc_backgrd_manage(pid, PROC_BACKGROUND_INACTIVE, OOMADJ_BACKGRD_UNLOCKED); break; case OOMADJ_INIT: ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED); @@ -434,10 +405,10 @@ int proc_set_active(int pid, int oom_score_adj) case OOMADJ_FOREGRD_LOCKED: case OOMADJ_BACKGRD_LOCKED: case OOMADJ_SU: - case OOMADJ_INIT: /* don't change oom value pid */ ret = -1; break; + case OOMADJ_INIT: case OOMADJ_FOREGRD_UNLOCKED: ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_LOCKED); break; @@ -461,7 +432,7 @@ int proc_set_active(int pid, int oom_score_adj) int proc_set_inactive(int pid, int oom_score_adj) { int ret = 0; - struct proc_process_info_t * processinfo; + struct proc_process_info_t * ppi; switch (oom_score_adj) { case OOMADJ_FOREGRD_UNLOCKED: case OOMADJ_BACKGRD_UNLOCKED: @@ -474,8 +445,8 @@ int proc_set_inactive(int pid, int oom_score_adj) ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED); break; case OOMADJ_BACKGRD_LOCKED: - processinfo = find_process_info(NULL, pid, NULL); - if (processinfo) + ppi = find_process_info(NULL, pid, NULL); + if (ppi) ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED); break; case OOMADJ_SERVICE_FOREGRD: @@ -496,15 +467,15 @@ int proc_set_inactive(int pid, int oom_score_adj) void proc_set_group(pid_t onwerpid, pid_t childpid) { int oom_score_adj = 0; - struct proc_process_info_t *process_info = + struct proc_process_info_t *ppi = find_process_info(NULL, onwerpid, NULL); if (proc_get_oom_score_adj(onwerpid, &oom_score_adj) < 0) { _D("owner pid(%d) was already terminated", onwerpid); return; } - if (process_info) { - proc_add_pid_list(process_info, childpid, RESOURCED_APP_TYPE_GROUP); + if (ppi) { + proc_add_pid_list(ppi, childpid, PROC_TYPE_GROUP); proc_set_oom_score_adj(childpid, oom_score_adj); } } diff --git a/src/proc-stat/proc-usage-stats-helper.c b/src/proc-stat/proc-usage-stats-helper.c new file mode 100644 index 00000000..a5a96e5c --- /dev/null +++ b/src/proc-stat/proc-usage-stats-helper.c @@ -0,0 +1,256 @@ +/* + * resourced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * @file proc-usage-stats-helper.c + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + */ + +#include <sys/types.h> +#include <unistd.h> + +#include "proc-usage-stats-helper.h" +#include "macro.h" +#include "trace.h" + +#define BtoKiB(bytes) (bytes >> 10) +#define kBtoKiB(kbytes) (int)(((long long)kbytes * 1024)/1000) + +#define TASK_NAME_BASE "runtime_info_%s_usage" + +static int proc_get_virtual_mem_size(int pid, int *vsize) +{ + FILE *proc_stat; + char buf[1024]; + unsigned long vsz; + + proc_stat = NULL; + + if (!vsize) + goto error; + + snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); + proc_stat = fopen(buf, "r"); + if (!proc_stat) + goto error; + + while (fgets(buf, sizeof(buf), proc_stat) != NULL) { + if (sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %lu", + &vsz) != 1) + goto error; + } + fclose(proc_stat); + + *vsize = BtoKiB(vsz); + return RESOURCED_ERROR_NONE; + +error: + if (proc_stat) + fclose(proc_stat); + _E("error reading /proc/%d/stat file", pid); + return RESOURCED_ERROR_FAIL; +} + +static int proc_get_smaps_info(int pid, struct process_memory_info_s *mem_info) +{ + FILE *smaps; + char buf[1024]; + int value; + + smaps = NULL; + + if (!mem_info) + goto error; + + snprintf(buf, sizeof(buf), "/proc/%d/smaps", pid); + smaps = fopen(buf, "r"); + if (!smaps) + goto error; + + while (fgets(buf, sizeof(buf), smaps) != NULL) { + if (sscanf(buf, "Rss: %d kB", &value) == 1) + mem_info->rss += kBtoKiB(value); + else if (sscanf(buf, "Pss: %d kB", &value) == 1) + mem_info->pss += kBtoKiB(value); + else if (sscanf(buf, "Shared_Clean: %d kB", &value) == 1) + mem_info->shared_clean += kBtoKiB(value); + else if (sscanf(buf, "Shared_Dirty: %d kB", &value) == 1) + mem_info->shared_dirty += kBtoKiB(value); + else if (sscanf(buf, "Private_Clean: %d kB", &value) == 1) + mem_info->private_clean += kBtoKiB(value); + else if (sscanf(buf, "Private_Dirty: %d kB", &value) == 1) + mem_info->private_dirty += kBtoKiB(value); + } + fclose(smaps); + + return RESOURCED_ERROR_NONE; + +error: + if (smaps) + fclose(smaps); + _E("error reading /proc/%d/smaps file", pid); + return RESOURCED_ERROR_FAIL; +} + +/* Helper functions to get the needed memory usage info. */ +void proc_get_memory_usage(int pid, struct process_memory_info_s *mem_info) +{ + if (!mem_info) + return; + + if (pid < 0) + goto error; + + memset(mem_info, 0, sizeof(struct process_memory_info_s)); + if (proc_get_virtual_mem_size(pid, &mem_info->vsz) != RESOURCED_ERROR_NONE) + goto error; + + if (proc_get_smaps_info(pid, mem_info) != RESOURCED_ERROR_NONE) + goto error; + + return; + +error: + mem_info->vsz = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->rss = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->pss = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->shared_clean = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->shared_dirty = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->private_clean = INVALID_PROCESS_INFO_FIELD_VALUE; + mem_info->private_dirty = INVALID_PROCESS_INFO_FIELD_VALUE; +} + +/* Helper functions to get the needed cpu usage info. */ +void proc_get_cpu_usage(int pid, struct process_cpu_usage_s *cpu_usage) +{ + unsigned long utime, stime; + FILE *proc_stat; + char buf[1024]; + + proc_stat = NULL; + + if (!cpu_usage) + return; + + if (pid < 0) + goto error; + + snprintf(buf, sizeof(buf), "/proc/%d/stat", pid); + proc_stat = fopen(buf, "r"); + if (!proc_stat) + goto error; + while (fgets(buf, sizeof(buf), proc_stat) != NULL) { + if (sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %lu %lu", + &utime, &stime) != 2) { + goto error; + } + } + fclose(proc_stat); + + cpu_usage->utime = (int)utime; + cpu_usage->stime = (int)stime; + return; + +error: + if (proc_stat) + fclose(proc_stat); + _E("error reading /proc/%d/stat file", pid); + cpu_usage->utime = INVALID_PROCESS_INFO_FIELD_VALUE; + cpu_usage->stime = INVALID_PROCESS_INFO_FIELD_VALUE; +} + +/* Helper function to read from usage_info struct and populate + * result according to the task type */ +int proc_read_from_usage_struct(void *usage_info_list, int index, + int *result, runtime_info_task_type type) +{ + if (!usage_info_list || !result || (index < 0)) { + _E("invalid input"); + return RESOURCED_ERROR_FAIL; + } + + if (type == RUNTIME_INFO_TASK_MEMORY) { + struct process_memory_info_s *mem_info; + + mem_info = (struct process_memory_info_s *)usage_info_list; + result[0] = mem_info[index].vsz; + result[1] = mem_info[index].rss; + result[2] = mem_info[index].pss; + result[3] = mem_info[index].shared_clean; + result[4] = mem_info[index].shared_dirty; + result[5] = mem_info[index].private_clean; + result[6] = mem_info[index].private_dirty; + } else { + struct process_cpu_usage_s *cpu_usage; + + cpu_usage = (struct process_cpu_usage_s *)usage_info_list; + result[0] = cpu_usage[index].utime; + result[1] = cpu_usage[index].stime; + } + + return RESOURCED_ERROR_NONE; +} + +/* Create task name according to the current time and + * set it to the input param task_name */ +void proc_get_task_name(char *task_name, int size, + runtime_info_task_type task_type) +{ + struct tm cur_tm; + time_t now; + char buf[TASK_NAME_SIZE]; + + snprintf(buf, sizeof(buf), TASK_NAME_BASE, + ((task_type == RUNTIME_INFO_TASK_MEMORY) ? "memory" : "cpu")); + + if (!task_name || size <= 0) + return; + + now = time(NULL); + if (localtime_r(&now, &cur_tm) == NULL) { + _E("Failed to get localtime"); + snprintf(task_name, size, "%s_%llu", + buf, (long long)now); + return; + } + + snprintf(task_name, size, "%s_%.4d%.2d%.2d_%.2d%.2d%.2d", + buf, (1900 + cur_tm.tm_year), + 1 + cur_tm.tm_mon, cur_tm.tm_mday, cur_tm.tm_hour, + cur_tm.tm_min, cur_tm.tm_sec); +} + +/* Helper function to free the runtime info task instance */ +void proc_free_runtime_info_task(struct runtime_info_task *rt_task) +{ + if (!rt_task) + return; + + if (rt_task->usage_info_list) + free(rt_task->usage_info_list); + + close(rt_task->pipe_fds[0]); + close(rt_task->pipe_fds[1]); + + dbus_message_unref(rt_task->task_msg); + + free(rt_task); +} diff --git a/src/proc-stat/proc-usage-stats.c b/src/proc-stat/proc-usage-stats.c new file mode 100644 index 00000000..bb6ce2e5 --- /dev/null +++ b/src/proc-stat/proc-usage-stats.c @@ -0,0 +1,378 @@ +/* + * resourced + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * @file proc-usage-stats.c + * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved. + * + * ** Handles the dbus method calls made by the runtime-info APIs + * ** The supported calls are for collection of memory and cpu usage of input processes + * ** The fields collected can be found in the process_memory_info_s and + * process_cpu_usage_s structs in the proc-usage-stats-helper.h file + * ** The working is as follows: + * * The dbus method is called from runtime-info API + * * For each request, a runtime_info_task instance is created + * * This instance contains info related to that request + * * A pipe is created for each request and an ecore handler is added for the read end + * * A thread is swapned for the request and this thread collects the needed info + * * After collection of the usage info, the thread writes the success status to the + * write end of the pipe + * * This activates the handler, which appends the collected usage info to the reply + * dbus message and sends it to the process making the method call + * * The reply message is as follows: + * * If everything succeeded, it is an array of memory or cpu usage info structs + * * If some of the PIDs are invalid, then the entries for those PIDs will be + * INVALID_PROCESS_FIELD_VALUE + * * If anything went wrong in the above steps, then the reply is an integer + * which contains the error value + * * In case there was any error writing to the pipe from the swapned thread, + * then there is no reply and the method call fails after the timeout + */ + +#include <sys/types.h> +#include <unistd.h> +#include <Ecore.h> +#include <errno.h> + +#include "proc-usage-stats.h" +#include "proc-usage-stats-helper.h" +#include "resourced.h" +#include "macro.h" +#include "trace.h" +#include "edbus-handler.h" + +#define BtoKiB(bytes) (bytes >> 10) +#define kBtoKiB(kbytes) (int)(((long long)kbytes * 1024)/1000) + +#define PROCESS_MEMORY_USAGE_METHOD "ProcMemoryUsage" +#define PROCESS_CPU_USAGE_METHOD "ProcCpuUsage" + +/** + * @brief DBus method to return the memory usage information of input processes + * @since_tizen 2.4 + * + * @param[in] obj The E_DBus_Object + * @param[in] msg The dbus message sent by the runtime info API. + * This should be an array of process IDs. + * + * @retval The response dbus message contains an array of structs + * (structure similar to process_memory_info_s). The structs contain the + * memory usage info fields for the processes (in the same order). + * For invalid process IDs, the fields of the process_memory_info_s struct + * will be set to INVALID_PROCESS_INFO_FIELD_VALUE. + * If the input dbus message does not contain array of integers or if there + * are errors in computation, collection and sending of usage info, then the + * response dbus message contains only an integer whose value will the error value. + */ +static DBusMessage *edbus_proc_memory_usage(E_DBus_Object *obj, DBusMessage *msg); + +/** + * @brief DBus method to return the cpu usage information of input processes + * @since_tizen 2.4 + * + * @param[in] obj The E_DBus_Object + * @param[in] msg The dbus message sent by the runtime info API. + * This should be an array of process IDs. + * + * @retval The response dbus message contains an array of structs + * (structure similar to process_cpu_usage_s). The structs contain the + * cpu usage info fields for the processes (in the same order). + * For invalid process IDs, the fields of the process_cpu_usage_s struct + * will be set to INVALID_PROCESS_INFO_FIELD_VALUE. + * If the input dbus message does not contain array of integers or if there + * are errors in computation, collection and sending of usage info, then the + * response dbus message contains only an integer whose value will the error value. + */ +static DBusMessage *edbus_proc_cpu_usage(E_DBus_Object *obj, DBusMessage *msg); + +/* edbus_methods to register with edbus */ +static const struct edbus_method edbus_methods[] = { + { PROCESS_MEMORY_USAGE_METHOD, "ai", NULL, edbus_proc_memory_usage }, + { PROCESS_CPU_USAGE_METHOD, "ai", NULL, edbus_proc_cpu_usage }, +}; + +/* Ecore file handler for the read end of the pipe. + * Receives the error status from the runtime info task thread and collects + * the usage info calculated and sends it in a dbus message or send an error dbus message back + * with the error status added to the message + */ +static Eina_Bool proc_runtime_info_task_cb(void *data, Ecore_Fd_Handler *fd_handler) +{ + int i, j, ret, rsize, struct_size; + int fd; + int result[7]; + DBusMessage *reply; + DBusMessageIter iter, iter_arr, iter_struct; + struct runtime_info_task *rt_task; + + /* In case of errors in ecore file hander, the returned dbus message + * contains only a failure value */ + rt_task = (struct runtime_info_task *)data; + if (!rt_task) { + _E("invalid input data"); + goto error; + } + + if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) { + _E("task %s: ecore_main_fd_handler_active_get_error", rt_task->task_name); + goto error; + } + + fd = ecore_main_fd_handler_fd_get(fd_handler); + if (fd < 0) { + _E("task %s: ecore_main_fd_handler_fd_get error", rt_task->task_name); + goto error; + } + + rsize = read(fd, &ret, sizeof(int)); + if (rsize != sizeof(int)) { + _E("task %s: error reading value from read end of pipe", rt_task->task_name); + goto error; + } + _D("task %s: received %d on the read end", rt_task->task_name, ret); + if (ret != RESOURCED_ERROR_NONE) { + _E("task %s: error in collection of information", rt_task->task_name); + goto error; + } + + /* Create a reply message with the needed structure */ + reply = dbus_message_new_method_return(rt_task->task_msg); + if (!reply) { + _E("task %s: out of memory to allocate for reply dbus message. not attempting again!!!", rt_task->task_name); + return ECORE_CALLBACK_CANCEL; + } + dbus_message_iter_init_append(reply, &iter); + if (rt_task->task_type == RUNTIME_INFO_TASK_MEMORY) { + struct_size = 7; + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(iiiiiii)", &iter_arr); + } else { + struct_size = 2; + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ii)", &iter_arr); + } + /* Populate the reply message with the usage info */ + for (i = 0; i < rt_task->task_size; ++i) { + dbus_message_iter_open_container(&iter_arr, DBUS_TYPE_STRUCT, NULL, &iter_struct); + + /* Write the fields of the usage info struct to an int array + * (this is so that the code of dbus message append could be more elegant) */ + ret = proc_read_from_usage_struct(rt_task->usage_info_list, i, result, rt_task->task_type); + if (ret != RESOURCED_ERROR_NONE) { + _E("task %s: error in reading from usage info struct", rt_task->task_name); + goto error; + } + + for (j = 0; j < struct_size; ++j) + dbus_message_iter_append_basic(&iter_struct, DBUS_TYPE_INT32, &result[j]); + + dbus_message_iter_close_container(&iter_arr, &iter_struct); + } + dbus_message_iter_close_container(&iter, &iter_arr); + goto send_message; + +error: + /* In case of error, return only a failure value in the reply dbus message */ + _D("task %s: error occured in collection of usage info, sending error message", rt_task->task_name); + reply = dbus_message_new_method_return(rt_task->task_msg); + ret = -EREMOTEIO; + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + +send_message: + /* Send the reply message back to the caller. Best effort feature. */ + _D("task %s: sending reply dbus message", rt_task->task_name); + ret = edbus_message_send(reply); + if (ret != RESOURCED_ERROR_NONE) + _E("task %s: sending message failed. not attempting again!!!", rt_task->task_name); + + proc_free_runtime_info_task(rt_task); + dbus_message_unref(reply); + return ECORE_CALLBACK_CANCEL; +} + +static int proc_runtime_info_task(struct runtime_info_task *rt_task) +{ + int i, ret; + int wsize; + + /* Populate the usage_info_list with the needed info. There can be no failures here. */ + if (rt_task->task_type == RUNTIME_INFO_TASK_MEMORY) { + struct process_memory_info_s *mem_info; + + mem_info = (struct process_memory_info_s *)rt_task->usage_info_list; + for (i = 0; i < rt_task->task_size; ++i) + proc_get_memory_usage(rt_task->pid_list[i], &mem_info[i]); + } else { + struct process_cpu_usage_s *cpu_usage; + + cpu_usage = (struct process_cpu_usage_s *)rt_task->usage_info_list; + for (i = 0; i < rt_task->task_size; ++i) + proc_get_cpu_usage(rt_task->pid_list[i], &cpu_usage[i]); + } + + /* Write to the write end of the pipe depending on the success of + * the info collection (currently this is always success) */ + ret = RESOURCED_ERROR_NONE; + wsize = write(rt_task->pipe_fds[1], &ret, sizeof(int)); + if (wsize != sizeof(int)) { + _E("task %s: error in writing to write end of pipe", rt_task->task_name); + return RESOURCED_ERROR_FAIL; + } + return RESOURCED_ERROR_NONE; +} + +/* Task thread start routine. Gathers needed info and writes it to + * the memory in the runtime_info_task instance. + */ +static void *proc_runtime_info_task_thread(void *arg) +{ + int ret; + struct runtime_info_task *rt_task = (struct runtime_info_task *)arg; + + if (!rt_task) { + _E("invalid arguments!"); + return NULL; + } + + ret = proc_runtime_info_task(rt_task); + _D("task %s: finished processing, task status %d", rt_task->task_name, ret); + + if (ret != RESOURCED_ERROR_NONE) { + /* TODO: write code to close fds and release the runtime_task_info memory */ + _E("task %s: request was not completed! no reply to be sent to runtime-info!!!", rt_task->task_name); + } + + return NULL; +} + +static DBusMessage *proc_runtime_info_request_handler(DBusMessage *msg, runtime_info_task_type type) +{ + int ret; + pthread_t task_thread; + struct runtime_info_task *rt_task; + DBusMessage *reply; + DBusMessageIter iter; + DBusError err; + dbus_bool_t bret; + Ecore_Fd_Handler *task_efd; + + rt_task = NULL; + + dbus_message_iter_init(msg, &iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_INT32) { + _E("wrong message arguments. expected array of integers"); + ret = -EIO; + goto error; + } + + /* Create runtime_info_task for current task */ + rt_task = (struct runtime_info_task *)malloc(sizeof(struct runtime_info_task)); + if (!rt_task) { + _E("out of memory: not able to create runtime_info_task"); + ret = -ENOMEM; + goto error; + } + + /* Populate fields of the runtime_info_task */ + proc_get_task_name(rt_task->task_name, sizeof(rt_task->task_name), + type); + rt_task->task_type = type; + dbus_message_ref(msg); + rt_task->task_msg = msg; + + _D("Received %s usage request, task name is %s", + (rt_task->task_type == RUNTIME_INFO_TASK_MEMORY) ? "memory" : "cpu", + rt_task->task_name); + + dbus_error_init(&err); + bret = dbus_message_get_args(rt_task->task_msg, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_INT32, + &rt_task->pid_list, &rt_task->task_size, DBUS_TYPE_INVALID); + if (!bret) { + _E("task %s: not able to extract list of process IDs from the dbus message", rt_task->task_name); + ret = -EIO; + goto error; + } + + rt_task->usage_info_list = NULL; + if (rt_task->task_type == RUNTIME_INFO_TASK_MEMORY) + rt_task->usage_info_list = (void *)malloc(sizeof(struct process_memory_info_s) * rt_task->task_size); + else + rt_task->usage_info_list = (void *)malloc(sizeof(struct process_cpu_usage_s) * rt_task->task_size); + if (!rt_task->usage_info_list) { + _E("task %s: out of memory: not able to create usage_info_list of rt_task", rt_task->task_name); + ret = -ENOMEM; + goto error; + } + + /* Create pipe between main loop and (to-be-created) task thread and add ecore file handler for the read end */ + ret = pipe(rt_task->pipe_fds); + if (ret) { + _E("task %s: error creating pipe.", rt_task->task_name); + ret = -EIO; + goto error; + } + task_efd = ecore_main_fd_handler_add(rt_task->pipe_fds[0], ECORE_FD_READ, + (Ecore_Fd_Cb)proc_runtime_info_task_cb, (void *)rt_task, NULL, NULL); + if (!task_efd) { + _E("task %s: error creating ecore file handler", rt_task->task_name); + ret = -EREMOTEIO; + goto error; + } + + /* Create task thread to complete requested task */ + ret = pthread_create(&task_thread, NULL, (void *)proc_runtime_info_task_thread, (void *)rt_task); + if (ret) { + _E("task %s: error creating task thread", rt_task->task_name); + ret = -EREMOTEIO; + goto error; + } else + pthread_detach(task_thread); + _D("task %s: created thread for task", rt_task->task_name); + dbus_error_free(&err); + return NULL; + +error: + /* In case of error, return only a failure value in the reply dbus message */ + _D("task %s: error occured, sending error reply message", rt_task->task_name); + reply = dbus_message_new_method_return(msg); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret); + + dbus_error_free(&err); + proc_free_runtime_info_task(rt_task); + return reply; +} + +static DBusMessage *edbus_proc_memory_usage(E_DBus_Object *obj, DBusMessage *msg) +{ + return proc_runtime_info_request_handler(msg, RUNTIME_INFO_TASK_MEMORY); +} + +static DBusMessage *edbus_proc_cpu_usage(E_DBus_Object *obj, DBusMessage *msg) +{ + return proc_runtime_info_request_handler(msg, RUNTIME_INFO_TASK_CPU); +} + +resourced_ret_c proc_usage_stats_init(void) +{ + int ret; + + ret = edbus_add_methods(RESOURCED_PATH_PROCESS, edbus_methods, ARRAY_SIZE(edbus_methods)); + return ret; +} diff --git a/src/resourced/init.c b/src/resourced/init.c index 0b1dc8d9..34cdfb2a 100644 --- a/src/resourced/init.c +++ b/src/resourced/init.c @@ -42,7 +42,6 @@ static struct daemon_opts opts = { 1, 1, - 1, COUNTER_UPDATE_PERIOD, FLUSH_PERIOD, RESOURCED_DEFAULT_STATE, @@ -153,19 +152,16 @@ static void sig_term_handler(int sig) { struct shared_modules_data *shared_data = get_shared_modules_data(); - opts.state |= RESOURCED_FORCIBLY_QUIT_STATE; _SD("sigterm or sigint received"); if (shared_data && shared_data->carg && shared_data->carg->ecore_timer) { + SET_BIT(shared_data->carg->opts->state, RESOURCED_FORCIBLY_QUIT_STATE); /* save data on exit, it's impossible to do in fini * module function, due it executes right after ecore stopped */ +#ifdef NETWORK_MODULE reschedule_count_timer(shared_data->carg, 0); - - /* Another way it's introduce another timer and quit main loop - * in it with waiting some event. */ - sleep(TIME_TO_SAFE_DATA); +#endif + ecore_timer_thaw(shared_data->darg->ecore_quit); } - - ecore_main_loop_quit(); } static void add_signal_handler(void) @@ -174,6 +170,13 @@ static void add_signal_handler(void) signal(SIGINT, sig_term_handler); } +static Eina_Bool quit_main_loop(void *user_data) +{ + _D("Quit main loop"); + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + int resourced_init(struct daemon_arg *darg) { int ret_code; @@ -190,6 +193,11 @@ int resourced_init(struct daemon_arg *darg) _D("argment swaptype = %s", swap_arg[opts.enable_swap]); add_signal_handler(); edbus_init(); + /* we couldn't create timer in signal callback, due ecore_timer_add + * alocates memory */ + darg->ecore_quit = ecore_timer_add(TIME_TO_SAFE_DATA, quit_main_loop, NULL); + ecore_timer_freeze(darg->ecore_quit); + return RESOURCED_ERROR_NONE; } diff --git a/src/resourced/init.h b/src/resourced/init.h index 000406cf..ec6e0614 100644 --- a/src/resourced/init.h +++ b/src/resourced/init.h @@ -27,6 +27,8 @@ #ifndef _RESOURCED_INIT_H #define _RESOURCED_INIT_H +#include <Ecore.h> + #include "resourced.h" #include "daemon-options.h" @@ -36,6 +38,7 @@ struct daemon_arg { int argc; char **argv; struct daemon_opts *opts; + Ecore_Timer *ecore_quit; }; int resourced_init(struct daemon_arg *darg); diff --git a/src/swap/swap.c b/src/swap/swap.c index 740bed61..06a7caf8 100644 --- a/src/swap/swap.c +++ b/src/swap/swap.c @@ -29,6 +29,8 @@ #include "swap-common.h" #include "notifier.h" #include "proc-process.h" +#include "proc-main.h" +#include "config-parser.h" #include <resourced.h> #include <trace.h> @@ -46,10 +48,14 @@ #define MAX_SWAP_VICTIMS 16 #define MEMCG_PATH "/sys/fs/cgroup/memory" + +#define BACKCG_PATH MEMCG_PATH"/background" +#define BACKCG_PROCS BACKCG_PATH"/cgroup.procs" + #define SWAPCG_PATH MEMCG_PATH"/swap" #define SWAPCG_PROCS SWAPCG_PATH"/cgroup.procs" #define SWAPCG_USAGE SWAPCG_PATH"/memory.usage_in_bytes" -#define SWAPCG_RECLAIM SWAPCG_PATH"/memory.force_reclaim" +#define SWAPCG_LIMIT SWAPCG_PATH"/memory.limit_in_bytes" #define SWAP_ON_EXEC_PATH "/sbin/swapon" #define SWAP_OFF_EXEC_PATH "/sbin/swapoff" @@ -59,6 +65,11 @@ #define SWAPFILE_NAME "/dev/zram0" +#define SWAP_CONF_FILE "/etc/resourced/swap.conf" +#define SWAP_CONTROL "SWAP_CONTROL" +#define SWAP_HARD_LIMIT "SWAP_HARD_LIMIT" +#define SWAP_HARD_LIMIT_DEFAULT 0.5 + #define SWAP_PATH_MAX 100 #define MBtoB(x) (x<<20) @@ -70,14 +81,19 @@ #define SWAP_TIMER_INTERVAL 0.5 #define SWAP_PRIORITY 20 -#define SWAP_COUNT_MAX 5 +#define SWAP_SORT_MAX 10 +#define SWAP_COUNT_MAX 5 -struct swap_data_type { - enum swap_status_type status_type; - unsigned long *args; +struct task_info { + pid_t pid; + pid_t pgid; + int oom_score_adj; + int size; + int cgroup_cnt; }; static int swapon; +static float hard_limit_fraction = SWAP_HARD_LIMIT_DEFAULT; static pthread_mutex_t swap_mutex; static pthread_cond_t swap_cond; static Ecore_Timer *swap_timer = NULL; @@ -85,37 +101,6 @@ static Ecore_Timer *swap_timer = NULL; static const struct module_ops swap_modules_ops; static const struct module_ops *swap_ops; -pid_t swap_victims[MAX_SWAP_VICTIMS]; - -static int swap_set_candidate_pid(void *data) -{ - unsigned long *args = data; - int i; - int *pid_array = (int*)args[0]; - int count = (int)args[1]; - - memset(swap_victims, 0, sizeof(int)*MAX_SWAP_VICTIMS); - - for (i = 0; i < count; i++) - swap_victims[i] = pid_array[i]; - - return RESOURCED_ERROR_NONE; -} - -static pid_t swap_get_candidate_pid(void) -{ - int i; - pid_t pid = 0; - - for (i = 0; i < MAX_SWAP_VICTIMS; i++) - if(swap_victims[i]) { - pid = swap_victims[i]; - swap_victims[i] = 0; - break; - } - return pid; -} - static int swap_get_swap_type(void) { struct shared_modules_data *modules_data = get_shared_modules_data(); @@ -134,88 +119,270 @@ static void swap_set_swap_type(int type) modules_data->swap_data.swaptype = type; } -static int swap_check_swap_pid(int pid) +static unsigned long swap_calculate_hard_limit(unsigned long swap_cg_usage) { - char buf[SWAP_PATH_MAX] = {0,}; - int swappid; - int ret = 0; - FILE *f; + return (unsigned long)(swap_cg_usage * hard_limit_fraction); +} - f = fopen(SWAPCG_PROCS, "r"); - if (!f) { - _E("%s open failed", SWAPCG_PROCS); - return RESOURCED_ERROR_FAIL; +static int load_swap_config(struct parse_result *result, void *user_data) +{ + int limit_value; + + if (!result) { + _E("Invalid parameter: result is NULL"); + return -EINVAL; } - while (fgets(buf, SWAP_PATH_MAX, f) != NULL) { - swappid = atoi(buf); - if (swappid == pid) { - ret = swappid; - break; + if (!strncmp(result->section, SWAP_CONTROL, strlen(SWAP_CONTROL))) { + if (!strncmp(result->name, SWAP_HARD_LIMIT, strlen(SWAP_CONTROL))) { + limit_value = (int)strtoul(result->value, NULL, 0); + if (limit_value < 0 || limit_value > 100) { + _E("Invalid %s value in %s file, setting %f as default percent value", + SWAP_HARD_LIMIT, SWAP_CONF_FILE, SWAP_HARD_LIMIT_DEFAULT); + return RESOURCED_ERROR_NONE; + } + + hard_limit_fraction = (float)limit_value/100; } } - fclose(f); - return ret; + _D("hard limit fraction for swap module is %f", hard_limit_fraction); + return RESOURCED_ERROR_NONE; } -static int swap_check_swap_cgroup(void) +static int swap_move_to_swap_cgroup(pid_t pid) { - char buf[SWAP_PATH_MAX] = {0,}; - int ret = SWAP_FALSE; + int size; FILE *f; + char buf[SWAP_PATH_MAX] = {0,}; - f = fopen(SWAPCG_PROCS, "r"); + f = fopen(SWAPCG_PROCS, "w"); if (!f) { - _E("%s open failed", SWAPCG_PROCS); + _E("Fail to %s file open", SWAPCG_PROCS); return RESOURCED_ERROR_FAIL; } - while (fgets(buf, SWAP_PATH_MAX, f) != NULL) { - ret = SWAP_TRUE; - break; - } + + _D("Moving task %d to swap cgroup", pid); + size = sprintf(buf, "%d", pid); + if (fwrite(buf, size, 1, f) != 1) + _E("fwrite cgroup tasks to swap cgroup failed : %d\n", pid); fclose(f); - return ret; + + return RESOURCED_ERROR_NONE; } -static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim) +/* + * check current mem usage total, and caculate to move swap cgroup + */ +static int swap_victims(GArray *victim_candidates) { - char buf[SWAP_PATH_MAX] = {0,}; - char appname[SWAP_PATH_MAX]; - pid_t pid = 0; - int size; - int swap_cg_cnt=0; - unsigned long usage, nr_to_reclaim; + int i, ret, loop_max; + int total_usage = 0; + int swap_size = 0; + struct sysinfo si; + char appname[PROC_NAME_MAX]; + + if (!sysinfo(&si)) + total_usage = si.totalram - si.freeram; + + if (!total_usage) { + _E("sysinfo failed"); + return RESOURCED_ERROR_FAIL; + } + + swap_size = total_usage >> 3; - /* check swap cgroup count */ - while (fgets(buf, SWAP_PATH_MAX, procs) != NULL) { + if (victim_candidates->len < SWAP_COUNT_MAX) + loop_max = victim_candidates->len; + else + loop_max = SWAP_COUNT_MAX; + + _D("Received %d victims to be moved to swap cgroup. Moving %d tasks", victim_candidates->len, loop_max); + for (i=0; i<loop_max; i++) { + struct task_info tsk; + + tsk = g_array_index(victim_candidates, struct task_info, i); + + if (i == 0) { + ret = proc_get_cmdline(tsk.pid, appname); + if (ret == RESOURCED_ERROR_FAIL) + continue; + /* To DO : kill highest 1 process */ + kill(tsk.pid, SIGKILL); + _E("we killed %d (%s)", tsk.pid, appname); + } + + swap_move_to_swap_cgroup(tsk.pid); + swap_size -= tsk.size; + + if (swap_size < 0) + return RESOURCED_ERROR_NONE; + } + + return RESOURCED_ERROR_NONE; +} + +/* + * sorting by mem usage and LRU + */ +static int swap_sort_LRU(const struct task_info *ta, const struct task_info *tb) +{ + /* sort by LRU */ + assert(ta != NULL); + assert(tb != NULL); + + return ((int)(tb->cgroup_cnt) - (int)(ta->cgroup_cnt)); +} + +static int swap_sort_usage(const struct task_info *ta, const struct task_info *tb) +{ + /* + * sort by task size + */ + assert(ta != NULL); + assert(tb != NULL); + + return ((int)(tb->size) - (int)(ta->size)); +} + +static bool get_mem_usage_by_pid(pid_t pid, unsigned int *rss) +{ + FILE *fp; + char proc_path[SWAP_PATH_MAX]; + + sprintf(proc_path, "/proc/%d/statm", pid); + fp = fopen(proc_path, "r"); + if (fp == NULL) + return false; + + if (fscanf(fp, "%*s %d", rss) < 1) { + fclose(fp); + return false; + } + + fclose(fp); + + /* convert page to Kb */ + *rss *= 4; + return true; +} + + +/* + * check background mem usage. + */ +static int swap_check_cgroup_victims(void) +{ + FILE *f = NULL; + int i = 0; + int cnt = 0; + int ret; + char buf[SWAP_PATH_MAX] = {0, }; + GArray *victim_candidates = NULL; + + victim_candidates = g_array_new(false, false, sizeof(struct task_info)); + + /* if g_array_new fails, return the current number of victims */ + if (victim_candidates == NULL) { + _E("victim_candidates failed"); + return RESOURCED_ERROR_OUT_OF_MEMORY; + } + + if (f == NULL) { + f = fopen(BACKCG_PROCS, "r"); + if (f == NULL) { + _E("%s open failed", BACKCG_PROCS); + return RESOURCED_ERROR_FAIL; + } + } + + while (fgets(buf, 32, f) != NULL) { + struct task_info new_victim; pid_t tpid = 0; int toom = 0; - int ret; + unsigned int tsize = 0; - if (!pid) { - tpid = atoi(buf); + tpid = atoi(buf); - if (proc_get_oom_score_adj(tpid, &toom) < 0) { - _D("pid(%d) was already terminated", tpid); - continue; - } + if (proc_get_oom_score_adj(tpid, &toom) < 0) { + _D("pid(%d) was already terminated", tpid); + continue; + } - if (toom < OOMADJ_BACKGRD_UNLOCKED) - continue; + if (!get_mem_usage_by_pid(tpid, &tsize)) { + _D("pid(%d) size is not available\n", tpid); + continue; + } - ret = proc_get_cmdline(tpid, appname); - if (ret == RESOURCED_ERROR_FAIL) - continue; + for (i = 0; i < victim_candidates->len; i++) { + struct task_info *tsk = &g_array_index(victim_candidates, + struct task_info, i); + if (getpgid(tpid) == tsk->pgid) { + tsk->size += tsize; + if (tsk->oom_score_adj <= 0 && toom > 0) { + tsk->pid = tpid; + tsk->oom_score_adj = toom; + tsk->cgroup_cnt = cnt; + } + break; + } + } - pid = tpid; + if (i == victim_candidates->len) { + new_victim.pid = tpid; + new_victim.pgid = getpgid(tpid); + new_victim.oom_score_adj = toom; + new_victim.size = tsize; + new_victim.cgroup_cnt = cnt; + + g_array_append_val(victim_candidates, new_victim); } - swap_cg_cnt++; + + cnt++; } - /* swap cgroup count is MAX, kill 1 process */ - if (swap_cg_cnt >= SWAP_COUNT_MAX) { - kill(pid, SIGKILL); - _E("we killed %d (%s)", pid, appname); + if (victim_candidates->len == 0) { + _E("victim_candidates->len = %d", victim_candidates->len); + g_array_free(victim_candidates, true); + fclose(f); + return RESOURCED_ERROR_NO_DATA; + } + + /* sort by mem usage */ + g_array_sort(victim_candidates, (GCompareFunc)swap_sort_usage); + + if (victim_candidates->len > SWAP_SORT_MAX) + g_array_remove_range(victim_candidates, SWAP_SORT_MAX, victim_candidates->len - SWAP_SORT_MAX); + + /* sort by LRU */ + g_array_sort(victim_candidates, (GCompareFunc)swap_sort_LRU); + + ret = swap_victims(victim_candidates); + if (ret) { + _E("swap_victims error"); + g_array_free(victim_candidates, true); + fclose(f); + return RESOURCED_ERROR_FAIL; + } + + g_array_free(victim_candidates, true); + + fclose(f); + return RESOURCED_ERROR_NONE; +} + +static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *limit_in_bytes) +{ + char buf[SWAP_PATH_MAX] = {0,}; + int size; + int ret; + unsigned long usage; + unsigned long swap_cg_limit; + + ret = swap_check_cgroup_victims(); + + if (ret < 0) { + _E("swap_check_cgroup_victims error"); + return RESOURCED_ERROR_FAIL; } /* cacluate reclaim size by usage and swap cgroup count */ @@ -224,11 +391,13 @@ static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim usage = (unsigned long)atol(buf); - nr_to_reclaim = BtoPAGE(usage) >> ((swap_cg_cnt >> 1) + 1); + swap_cg_limit = swap_calculate_hard_limit(usage); + _D("swap cgroup usage is %lu, hard limit set to %lu (hard limit fraction %f)", + usage, swap_cg_limit, hard_limit_fraction); /* set reclaim size */ - size = sprintf(buf, "%lu", nr_to_reclaim); - if (fwrite(buf, 1, size, force_reclaim) != size) + size = sprintf(buf, "%lu", swap_cg_limit); + if (fwrite(buf, 1, size, limit_in_bytes) != size) _E("fwrite %s\n", buf); return RESOURCED_ERROR_NONE; @@ -236,37 +405,31 @@ static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim static void *swap_thread_main(void * data) { - FILE *procs = NULL; - FILE *usage_in_bytes = NULL; - FILE *force_reclaim = NULL; + FILE *procs; + FILE *usage_in_bytes; + FILE *limit_in_bytes; setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY); + procs = fopen(SWAPCG_PROCS, "r"); if (procs == NULL) { - procs = fopen(SWAPCG_PROCS, "r"); - if (procs == NULL) { - _E("%s open failed", SWAPCG_PROCS); - return NULL; - } + _E("%s open failed", SWAPCG_PROCS); + return NULL; } + usage_in_bytes = fopen(SWAPCG_USAGE, "r"); if (usage_in_bytes == NULL) { - usage_in_bytes = fopen(SWAPCG_USAGE, "r"); - if (usage_in_bytes == NULL) { - _E("%s open failed", SWAPCG_USAGE); - fclose(procs); - return NULL; - } + _E("%s open failed", SWAPCG_USAGE); + fclose(procs); + return NULL; } - if (force_reclaim == NULL) { - force_reclaim = fopen(SWAPCG_RECLAIM, "w"); - if (force_reclaim == NULL) { - _E("%s open failed", SWAPCG_RECLAIM); - fclose(procs); - fclose(usage_in_bytes); - return NULL; - } + limit_in_bytes = fopen(SWAPCG_LIMIT, "w"); + if (limit_in_bytes == NULL) { + _E("%s open failed", SWAPCG_LIMIT); + fclose(procs); + fclose(usage_in_bytes); + return NULL; } while (1) { @@ -281,10 +444,10 @@ static void *swap_thread_main(void * data) fseek(procs, 0, SEEK_SET); fseek(usage_in_bytes, 0, SEEK_SET); - fseek(force_reclaim, 0, SEEK_SET); + fseek(limit_in_bytes, 0, SEEK_SET); _D("swap_thread_do start"); - swap_thread_do(procs, usage_in_bytes, force_reclaim); + swap_thread_do(procs, usage_in_bytes, limit_in_bytes); _D("swap_thread_do end"); pthread_mutex_unlock(&swap_mutex); } @@ -293,18 +456,45 @@ static void *swap_thread_main(void * data) fclose(procs); if (usage_in_bytes) fclose(usage_in_bytes); - if (force_reclaim) - fclose(force_reclaim); + if (limit_in_bytes) + fclose(limit_in_bytes); return NULL; } +static pid_t swap_on(void) +{ + pid_t pid = fork(); + + if (pid == 0) { + execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d", SWAPFILE_NAME, (char *)NULL); + exit(0); + } + swapon = 1; + return pid; +} + +static pid_t swap_off(void) +{ + pid_t pid = fork(); + + if (pid == 0) { + execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH, SWAPFILE_NAME, (char *)NULL); + exit(0); + } + swapon = 0; + return pid; +} + static Eina_Bool swap_send_signal(void *data) { int ret; _D("swap timer callback function start"); + if(!swapon) + swap_on(); + /* signal to swap_start to start swap */ ret = pthread_mutex_trylock(&swap_mutex); @@ -354,82 +544,28 @@ static int swap_thread_create(void) return ret; } -static int get_swap_status(void) -{ - return swapon; -} - -static pid_t swap_on(void) -{ - pid_t pid = fork(); - - if (pid == 0) { - execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d", SWAPFILE_NAME, (char *)NULL); - exit(0); - } - swapon = 1; - return pid; -} - -static pid_t swap_off(void) +int swap_check_swap_pid(int pid) { - pid_t pid = fork(); - - if (pid == 0) { - execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH, SWAPFILE_NAME, (char *)NULL); - exit(0); - } - swapon = 0; - return pid; -} - -static int restart_swap(void *data) -{ - int status; - pid_t pid; - pid_t ret; - - if (!swapon) { - swap_on(); - return RESOURCED_ERROR_NONE; - } - - pid = fork(); - if (pid == -1) { - _E("fork() error"); - return RESOURCED_ERROR_FAIL; - } else if (pid == 0) { - pid = swap_off(); - ret = waitpid(pid, &status, 0); - if (ret == -1) { - _E("Error waiting for swap_off child process (PID: %d, status: %d)", (int)pid, status); - } - swap_on(); - exit(0); - } - swapon = 1; - - return RESOURCED_ERROR_NONE; -} - -static int swap_move_swap_cgroup(void *data) -{ - int *args = data; - int size; - FILE *f; char buf[SWAP_PATH_MAX] = {0,}; + int swappid; + int ret = 0; + FILE *f; - f = fopen(SWAPCG_PROCS, "w"); + f = fopen(SWAPCG_PROCS, "r"); if (!f) { - _E("Fail to %s file open", SWAPCG_PROCS); + _E("%s open failed", SWAPCG_PROCS); return RESOURCED_ERROR_FAIL; } - size = sprintf(buf, "%d", *args); - if (fwrite(buf, size, 1, f) != 1) - _E("fwrite cgroup tasks to swap cgroup failed : %d\n", *args); - fclose(f); - return RESOURCED_ERROR_NONE; + while (fgets(buf, SWAP_PATH_MAX, f) != NULL) { + swappid = atoi(buf); + if (swappid == pid) { + ret = swappid; + break; + } + } + fclose(f); + return ret; } static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg) @@ -452,10 +588,8 @@ static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg) } _I("swap cgroup entered : pid : %d", (int)pid); - swap_move_swap_cgroup(&pid); + swap_move_to_swap_cgroup(pid); - if (get_swap_status() == SWAP_OFF) - restart_swap(NULL); swap_start(NULL); } @@ -486,7 +620,8 @@ static void swap_type_edbus_signal_handler(void *data, DBusMessage *msg) break; case 1: if (swap_get_swap_type() != SWAP_ON) { - restart_swap(NULL); + if (!swapon) + swap_on(); swap_set_swap_type(type); } break; @@ -522,10 +657,10 @@ static void swap_dbus_init(void) register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_TYPE, - (void *)swap_type_edbus_signal_handler); + (void *)swap_type_edbus_signal_handler, NULL); register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_START_PID, - (void *)swap_start_pid_edbus_signal_handler); + (void *)swap_start_pid_edbus_signal_handler, NULL); ret = edbus_add_methods(RESOURCED_PATH_SWAP, edbus_methods, ARRAY_SIZE(edbus_methods)); @@ -554,9 +689,9 @@ static int swap_init(void) static int swap_check_node(void) { - FILE *procs = NULL; - FILE *usage_in_bytes = NULL; - FILE *force_reclaim = NULL; + FILE *procs; + FILE *usage_in_bytes; + FILE *limit_in_bytes; procs = fopen(SWAPCG_PROCS, "r"); if (procs == NULL) { @@ -574,13 +709,13 @@ static int swap_check_node(void) fclose(usage_in_bytes); - force_reclaim = fopen(SWAPCG_RECLAIM, "w"); - if (force_reclaim == NULL) { - _E("%s open failed", SWAPCG_RECLAIM); + limit_in_bytes = fopen(SWAPCG_LIMIT, "w"); + if (limit_in_bytes == NULL) { + _E("%s open failed", SWAPCG_LIMIT); return RESOURCED_ERROR_NO_DATA; } - fclose(force_reclaim); + fclose(limit_in_bytes); return RESOURCED_ERROR_NONE; } @@ -590,82 +725,40 @@ static int resourced_swap_check_runtime_support(void *data) return swap_check_node(); } -static int resourced_swap_status(void *data) -{ - int ret = RESOURCED_ERROR_NONE; - struct swap_data_type *s_data; - - s_data = (struct swap_data_type *)data; - switch(s_data->status_type) { - case SWAP_GET_TYPE: - ret = swap_get_swap_type(); - break; - case SWAP_GET_CANDIDATE_PID: - ret = swap_get_candidate_pid(); - break; - case SWAP_GET_STATUS: - ret = get_swap_status(); - break; - case SWAP_CHECK_PID: - if (s_data->args) - ret = swap_check_swap_pid((int)s_data->args[0]); - else - ret = RESOURCED_ERROR_FAIL; - break; - case SWAP_CHECK_CGROUP: - ret = swap_check_swap_cgroup(); - break; - } - return ret; -} - static int resourced_swap_init(void *data) { struct modules_arg *marg = (struct modules_arg *)data; struct daemon_opts *dopt = marg->opts; + int ret; + + ret = config_parse(SWAP_CONF_FILE, load_swap_config, NULL); + + if (ret < 0) + return ret; swap_ops = &swap_modules_ops; if (dopt->enable_swap) swap_set_swap_type(dopt->enable_swap); - register_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid); register_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start); - register_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap); - register_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup); return swap_init(); } static int resourced_swap_finalize(void *data) { - unregister_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid); unregister_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start); - unregister_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap); - unregister_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup); return RESOURCED_ERROR_NONE; } -int swap_status(enum swap_status_type type, unsigned long *args) -{ - struct swap_data_type s_data; - - if (!swap_ops) - return RESOURCED_ERROR_NONE; - - s_data.status_type = type; - s_data.args = args; - return swap_ops->status(&s_data); -} - static const struct module_ops swap_modules_ops = { .priority = MODULE_PRIORITY_NORMAL, .name = "swap", .init = resourced_swap_init, .exit = resourced_swap_finalize, .check_runtime_support = resourced_swap_check_runtime_support, - .status = resourced_swap_status, }; MODULE_REGISTER(&swap_modules_ops) diff --git a/src/swap/swap.conf b/src/swap/swap.conf new file mode 100644 index 00000000..74acdb86 --- /dev/null +++ b/src/swap/swap.conf @@ -0,0 +1,5 @@ +#Swap module config file +[SWAP_CONTROL] +#Swap metrics control +#SWAP_HARD_LIMIT is the hard limit percent value (should be an integer). Default is 50 (50%). +SWAP_HARD_LIMIT=50 diff --git a/src/utils/datausage-tool.c b/src/utils/datausage-tool.c index 24196773..3af105d5 100644 --- a/src/utils/datausage-tool.c +++ b/src/utils/datausage-tool.c @@ -29,8 +29,8 @@ #include <string.h> #include "data_usage.h" +#include "macro.h" #include "resourced.h" -#include "rd-network.h" #include "const.h" #include "iface.h" #include "config.h" @@ -59,6 +59,8 @@ struct arg_param { int64_t send_limit; resourced_roaming_type roaming_type; char *app_id; + char *imsi; + resourced_state_t ground; }; static resourced_ret_c convert_roaming(const char *str, @@ -131,12 +133,14 @@ static void print_usage() puts(" -q [--quota] <appid> "); puts(" -Q [--remove-quota] <appid> "); puts(" -s [--rst-state] <pkgid> "); + puts(" -I [--imsi] sim id "); + puts(" -B [--background] background attribute, used for quota "); } static enum run_rsml_cmd parse_cmd(int argc, char **argv, struct arg_param *param) { - const char *optstring = "hvla:e:g:uf:t:i:d::G:R:r:O:q:Q:M:s:"; + const char *optstring = "hvla:e:g:uf:t:i:d::G:R:r:O:q:Q:M:s:I:B"; const struct option options[] = { {"help", no_argument, 0, 'h'}, @@ -158,6 +162,8 @@ static enum run_rsml_cmd parse_cmd(int argc, char **argv, {"remove-quota", required_argument, 0, 'Q'}, {"roaming", required_argument, 0, 'M'}, {"rst-state", required_argument, 0, 's'}, + {"imsi", required_argument, 0, 'I'}, + {"background", no_argument, 0, 'B'}, {0, 0, 0, 0} }; @@ -326,6 +332,16 @@ static enum run_rsml_cmd parse_cmd(int argc, char **argv, cmd = RESOURCED_RESTRICTION_STATE; param->app_id = strdup(optarg); break; + case 'I': + if (!optarg) { + printf("Remove quota option requeres an argument."); + exit(EXIT_FAILURE); + } + param->imsi = strdup(optarg); + break; + case 'B': + param->ground = RESOURCED_STATE_BACKGROUND; + break; default: printf("Unknown option %c\n", (char)retval); print_usage(); @@ -342,24 +358,37 @@ static enum run_rsml_cmd parse_cmd(int argc, char **argv, */ resourced_cb_ret data_usage_callback(const data_usage_info *info, void *user_data) { + execute_once { + printf("%*s|%16s|%16s|%10s|%10s|%10s|%10s|%3s|%3s|%10s|%20s...\n", + user_data ? 3 : 20, + user_data ? "ift" : "app_id", + "from", "to", "fr_rx", "bg_rx", + "fr_tx", "bg_tx", "rmg", + "hnp", "ifname", "imsi"); + } + /*TODO rewrite this hack*/ if (user_data) - printf("iftype %d\n", info->iftype); + printf("%3d|", info->iftype); else - printf("%s\n", info->app_id ? info->app_id : UNKNOWN_APP); + printf("%20s|", info->app_id ? info->app_id : UNKNOWN_APP); if (info->interval) { - printf("%s\t", ctime(&info->interval->from)); - printf("%s\t", ctime(&info->interval->to)); + char s[20] = {0}; + struct tm *l = localtime(&info->interval->from); + strftime(s, sizeof(s), "%a, %b %d %Y", l); + printf("%17s|", s); + l = localtime(&info->interval->to); + strftime(s, sizeof(s), "%a, %b %d %Y", l); + printf("%17s|", s); } else - printf("<entire interval>\t"); + printf("%35s|", "<entire interval>"); - printf("%ld/%ld\t%ld/%ld/%u/%u/%s\n", info->foreground.cnt.incoming_bytes, - info->background.cnt.incoming_bytes, - info->foreground.cnt.outgoing_bytes, - info->background.cnt.outgoing_bytes, + printf("%10ld|%10ld|%3u|%3u|%10s|%20s\n", info->cnt.incoming_bytes, + info->cnt.outgoing_bytes, info->roaming, info->hw_net_protocol_type, - info->ifname); + info->ifname, + info->imsi); return RESOURCED_CONTINUE; } @@ -374,10 +403,10 @@ resourced_cb_ret restriction_callback(const resourced_restriction_info *info, void *user_data) { printf("appid: %s, iftype: %d, rst_state %d, rcv_limit %d, " - "send_limit %d, roaming %d\n", + "send_limit %d, roaming %d, quota_id %d\n", info->app_id ? info->app_id : UNKNOWN_APP, info->iftype, info->rst_state, - info->rcv_limit, info->send_limit, info->roaming); + info->rcv_limit, info->send_limit, info->roaming, info->quota_id); return RESOURCED_CONTINUE; } @@ -513,20 +542,21 @@ int main(int argc, char **argv) case RESOURCED_SET_QUOTA: { data_usage_quota quota = { 0 }; - if (!param.du_rule.from || !param.du_rule.to) { - fprintf(stderr, "Quota command requires all of this options: " - "--from, --to and --roaming\n"); - break; - } - + time_t quota_start_time = 0; /* TODO in case of refactoring, use internal command line structure instead of public structure for holding param */ - time_t quota_start_time = time(NULL); - quota.start_time = "a_start_time; + if (param.du_rule.from) + quota.start_time = ¶m.du_rule.from; + else { + quota_start_time = time(NULL); + quota.start_time = "a_start_time; + } quota.snd_quota = param.send_limit; quota.rcv_quota = param.rcv_limit; quota.iftype = param.du_rule.iftype; - quota.time_period = param.du_rule.to - param.du_rule.from; + quota.time_period = param.du_rule.granularity; quota.roaming_type = param.roaming_type; + quota.imsi = param.imsi; + quota.quota_type = param.ground; if (set_datausage_quota(param.app_id, "a) != RESOURCED_ERROR_NONE) { fprintf(stderr, "Failed to apply quota!\n"); @@ -540,6 +570,8 @@ int main(int argc, char **argv) rule.app_id = param.app_id; rule.iftype = param.du_rule.iftype; rule.roaming = param.roaming_type; + rule.imsi = param.imsi; + rule.quota_type = param.ground; if (remove_datausage_quota(&rule) != RESOURCED_ERROR_NONE) { fprintf(stderr, "Failed to remove quota!\n"); |