--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+SET(fw_name "resourced")
+SET(RESOURCED resourced)
+SET(LIBS libs)
+SET(PROC-STAT proc-stat)
+SET(NETWORK rd-network)
+
+PROJECT(${fw_name})
+
+#Set CFLAGS
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -DRESOURCED_BUILD -Wall -Werror -fvisibility=hidden")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -DRESOURCED_BUILD -Wall -Werror -fvisibility=hidden")
+
+#Set as-needed
+SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed")
+
+#Set debug-mode if needed
+IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
+ STRING(REGEX REPLACE "-O2" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
+ STRING(REGEX REPLACE "-D_FORTIFY_SOURCE=2" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
+ ADD_DEFINITIONS(-DNETWORK_DEBUG_ENABLED)
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -O0 -g")
+ SET(CMAKE_VERBOSE_MAKEFILE ON)
+ SET(VERBOSE 1)
+ELSE()
+#set compile size optimization option in case of none DEBUG
+ SET(ADDITIONAL_OFLAGS "-fdata-sections -ffunction-sections -Wl,--gc-sections -fno-exceptions")
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${ADDITIONAL_OFLAGS}")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ADDITIONAL_OFLAGS}")
+ENDIF()
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+#following section is needed for pkg-config *.in file
+SET(LIBDIR ${PREFIX}/lib)
+SET(INCLUDEDIR ${PREFIX}/include)
+SET(PC_NAME lib${RESOURCED})
+SET(PC_REQUIRED "glib-2.0 vconf vconf-internal-keys sqlite3 dlog eina edbus")
+SET(PC_PROVIDED_LIBS "-l${PROC-STAT} -l${RESOURCED}")
+
+SET(PC_CFLAGS -I\${includedir}/system)
+SET(VERSION ${FULLVER})
+
+CONFIGURE_FILE(
+ lib${RESOURCED}.pc.in
+ ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.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})
+
+SET(SOURCE_DIR ${CMAKE_SOURCE_DIR}/src)
+SET(TEST_DIR ${SOURCE_DIR}/test)
+SET(UTILS_SOURCE_DIR ${SOURCE_DIR}/utils)
+SET(RESOURCED_SOURCE_DIR ${SOURCE_DIR}/resourced)
+SET(PROC-STAT_SOURCE_DIR ${SOURCE_DIR}/proc-stat)
+SET(SLUGGISH_SOURCE_DIR ${SOURCE_DIR}/sluggish)
+SET(MEMORY_SOURCE_DIR ${SOURCE_DIR}/memory)
+SET(MEMPS_SOURCE_DIR ${SOURCE_DIR}/memps)
+SET(SWAP_SOURCE_DIR ${SOURCE_DIR}/swap)
+SET(MODULES_SOURCE_DIR ${SOURCE_DIR}/modules)
+SET(FREEZER_SOURCE_DIR ${SOURCE_DIR}/freezer)
+SET(HEART_SOURCE_DIR ${SOURCE_DIR}/heart)
+SET(NETWORK_SOURCE_DIR ${SOURCE_DIR}/network)
+SET(COMMON_SOURCE_DIR ${SOURCE_DIR}/common)
+SET(CPU_SOURCE_DIR ${SOURCE_DIR}/cpu)
+SET(VIP_SOURCE_DIR ${SOURCE_DIR}/vip-agent)
+SET(TIMER_SOURCE_DIR ${SOURCE_DIR}/timer-slack)
+SET(BLOCK_SOURCE_DIR ${SOURCE_DIR}/block)
+SET(RESOURCED_DBUS_SOURCE_DIR ${SOURCE_DIR}/resourced-dbus)
+
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION lib/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)
+
+ADD_SUBDIRECTORY(src)
--- /dev/null
+ Apache License\r
+ Version 2.0, January 2004\r
+ http://www.apache.org/licenses/\r
+\r
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r
+\r
+ 1. Definitions.\r
+\r
+ "License" shall mean the terms and conditions for use, reproduction,\r
+ and distribution as defined by Sections 1 through 9 of this document.\r
+\r
+ "Licensor" shall mean the copyright owner or entity authorized by\r
+ the copyright owner that is granting the License.\r
+\r
+ "Legal Entity" shall mean the union of the acting entity and all\r
+ other entities that control, are controlled by, or are under common\r
+ control with that entity. For the purposes of this definition,\r
+ "control" means (i) the power, direct or indirect, to cause the\r
+ direction or management of such entity, whether by contract or\r
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the\r
+ outstanding shares, or (iii) beneficial ownership of such entity.\r
+\r
+ "You" (or "Your") shall mean an individual or Legal Entity\r
+ exercising permissions granted by this License.\r
+\r
+ "Source" form shall mean the preferred form for making modifications,\r
+ including but not limited to software source code, documentation\r
+ source, and configuration files.\r
+\r
+ "Object" form shall mean any form resulting from mechanical\r
+ transformation or translation of a Source form, including but\r
+ not limited to compiled object code, generated documentation,\r
+ and conversions to other media types.\r
+\r
+ "Work" shall mean the work of authorship, whether in Source or\r
+ Object form, made available under the License, as indicated by a\r
+ copyright notice that is included in or attached to the work\r
+ (an example is provided in the Appendix below).\r
+\r
+ "Derivative Works" shall mean any work, whether in Source or Object\r
+ form, that is based on (or derived from) the Work and for which the\r
+ editorial revisions, annotations, elaborations, or other modifications\r
+ represent, as a whole, an original work of authorship. For the purposes\r
+ of this License, Derivative Works shall not include works that remain\r
+ separable from, or merely link (or bind by name) to the interfaces of,\r
+ the Work and Derivative Works thereof.\r
+\r
+ "Contribution" shall mean any work of authorship, including\r
+ the original version of the Work and any modifications or additions\r
+ to that Work or Derivative Works thereof, that is intentionally\r
+ submitted to Licensor for inclusion in the Work by the copyright owner\r
+ or by an individual or Legal Entity authorized to submit on behalf of\r
+ the copyright owner. For the purposes of this definition, "submitted"\r
+ means any form of electronic, verbal, or written communication sent\r
+ to the Licensor or its representatives, including but not limited to\r
+ communication on electronic mailing lists, source code control systems,\r
+ and issue tracking systems that are managed by, or on behalf of, the\r
+ Licensor for the purpose of discussing and improving the Work, but\r
+ excluding communication that is conspicuously marked or otherwise\r
+ designated in writing by the copyright owner as "Not a Contribution."\r
+\r
+ "Contributor" shall mean Licensor and any individual or Legal Entity\r
+ on behalf of whom a Contribution has been received by Licensor and\r
+ subsequently incorporated within the Work.\r
+\r
+ 2. Grant of Copyright License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ copyright license to reproduce, prepare Derivative Works of,\r
+ publicly display, publicly perform, sublicense, and distribute the\r
+ Work and such Derivative Works in Source or Object form.\r
+\r
+ 3. Grant of Patent License. Subject to the terms and conditions of\r
+ this License, each Contributor hereby grants to You a perpetual,\r
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r
+ (except as stated in this section) patent license to make, have made,\r
+ use, offer to sell, sell, import, and otherwise transfer the Work,\r
+ where such license applies only to those patent claims licensable\r
+ by such Contributor that are necessarily infringed by their\r
+ Contribution(s) alone or by combination of their Contribution(s)\r
+ with the Work to which such Contribution(s) was submitted. If You\r
+ institute patent litigation against any entity (including a\r
+ cross-claim or counterclaim in a lawsuit) alleging that the Work\r
+ or a Contribution incorporated within the Work constitutes direct\r
+ or contributory patent infringement, then any patent licenses\r
+ granted to You under this License for that Work shall terminate\r
+ as of the date such litigation is filed.\r
+\r
+ 4. Redistribution. You may reproduce and distribute copies of the\r
+ Work or Derivative Works thereof in any medium, with or without\r
+ modifications, and in Source or Object form, provided that You\r
+ meet the following conditions:\r
+\r
+ (a) You must give any other recipients of the Work or\r
+ Derivative Works a copy of this License; and\r
+\r
+ (b) You must cause any modified files to carry prominent notices\r
+ stating that You changed the files; and\r
+\r
+ (c) You must retain, in the Source form of any Derivative Works\r
+ that You distribute, all copyright, patent, trademark, and\r
+ attribution notices from the Source form of the Work,\r
+ excluding those notices that do not pertain to any part of\r
+ the Derivative Works; and\r
+\r
+ (d) If the Work includes a "NOTICE" text file as part of its\r
+ distribution, then any Derivative Works that You distribute must\r
+ include a readable copy of the attribution notices contained\r
+ within such NOTICE file, excluding those notices that do not\r
+ pertain to any part of the Derivative Works, in at least one\r
+ of the following places: within a NOTICE text file distributed\r
+ as part of the Derivative Works; within the Source form or\r
+ documentation, if provided along with the Derivative Works; or,\r
+ within a display generated by the Derivative Works, if and\r
+ wherever such third-party notices normally appear. The contents\r
+ of the NOTICE file are for informational purposes only and\r
+ do not modify the License. You may add Your own attribution\r
+ notices within Derivative Works that You distribute, alongside\r
+ or as an addendum to the NOTICE text from the Work, provided\r
+ that such additional attribution notices cannot be construed\r
+ as modifying the License.\r
+\r
+ You may add Your own copyright statement to Your modifications and\r
+ may provide additional or different license terms and conditions\r
+ for use, reproduction, or distribution of Your modifications, or\r
+ for any such Derivative Works as a whole, provided Your use,\r
+ reproduction, and distribution of the Work otherwise complies with\r
+ the conditions stated in this License.\r
+\r
+ 5. Submission of Contributions. Unless You explicitly state otherwise,\r
+ any Contribution intentionally submitted for inclusion in the Work\r
+ by You to the Licensor shall be under the terms and conditions of\r
+ this License, without any additional terms or conditions.\r
+ Notwithstanding the above, nothing herein shall supersede or modify\r
+ the terms of any separate license agreement you may have executed\r
+ with Licensor regarding such Contributions.\r
+\r
+ 6. Trademarks. This License does not grant permission to use the trade\r
+ names, trademarks, service marks, or product names of the Licensor,\r
+ except as required for reasonable and customary use in describing the\r
+ origin of the Work and reproducing the content of the NOTICE file.\r
+\r
+ 7. Disclaimer of Warranty. Unless required by applicable law or\r
+ agreed to in writing, Licensor provides the Work (and each\r
+ Contributor provides its Contributions) on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r
+ implied, including, without limitation, any warranties or conditions\r
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r
+ PARTICULAR PURPOSE. You are solely responsible for determining the\r
+ appropriateness of using or redistributing the Work and assume any\r
+ risks associated with Your exercise of permissions under this License.\r
+\r
+ 8. Limitation of Liability. In no event and under no legal theory,\r
+ whether in tort (including negligence), contract, or otherwise,\r
+ unless required by applicable law (such as deliberate and grossly\r
+ negligent acts) or agreed to in writing, shall any Contributor be\r
+ liable to You for damages, including any direct, indirect, special,\r
+ incidental, or consequential damages of any character arising as a\r
+ result of this License or out of the use or inability to use the\r
+ Work (including but not limited to damages for loss of goodwill,\r
+ work stoppage, computer failure or malfunction, or any and all\r
+ other commercial damages or losses), even if such Contributor\r
+ has been advised of the possibility of such damages.\r
+\r
+ 9. Accepting Warranty or Additional Liability. While redistributing\r
+ the Work or Derivative Works thereof, You may choose to offer,\r
+ and charge a fee for, acceptance of support, warranty, indemnity,\r
+ or other liability obligations and/or rights consistent with this\r
+ License. However, in accepting such obligations, You may act only\r
+ on Your own behalf and on Your sole responsibility, not on behalf\r
+ of any other Contributor, and only if You agree to indemnify,\r
+ defend, and hold each Contributor harmless for any liability\r
+ incurred by, or claims asserted against, such Contributor by reason\r
+ of your accepting any such warranty or additional liability.\r
+\r
+ END OF TERMS AND CONDITIONS\r
+\r
+ APPENDIX: How to apply the Apache License to your work.\r
+\r
+ To apply the Apache License to your work, attach the following\r
+ boilerplate notice, with the fields enclosed by brackets "[]"\r
+ replaced with your own identifying information. (Don't include\r
+ the brackets!) The text should be enclosed in the appropriate\r
+ comment syntax for the file format. We also recommend that a\r
+ file or class name and description of purpose be included on the\r
+ same "printed page" as the copyright notice for easier\r
+ identification within third-party archives.\r
+\r
+ Copyright [yyyy] [name of copyright owner]\r
+\r
+ Licensed under the Apache License, Version 2.0 (the "License");\r
+ you may not use this file except in compliance with the License.\r
+ You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+\r
+\r
--- /dev/null
+MACRO(INSTALL_SYMLINK _filepath _sympath)
+ GET_FILENAME_COMPONENT(_symname ${_sympath} NAME)
+ GET_FILENAME_COMPONENT(_installdir ${_sympath} PATH)
+
+ EXECUTE_PROCESS(COMMAND "${CMAKE_COMMAND}" -E create_symlink
+ ${_filepath}
+ ${CMAKE_CURRENT_BINARY_DIR}/${_symname})
+ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_symname}
+ DESTINATION ${_installdir})
+ENDMACRO(INSTALL_SYMLINK)
--- /dev/null
+MACRO(PROCESS_GPERF _input _output)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${_output}
+ COMMAND gperf ${_input} > ${_output}
+ DEPENDS ${_input})
+ENDMACRO(PROCESS_GPERF)
--- /dev/null
+PRAGMA journal_mode = PERSIST;
+INSERT INTO restrictions VALUES("com.samsung.easysignup", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.osmeta.runtime", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.osmeta.runtime.service", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.email", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-composer-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-viewer-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-conversation-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-mailbox-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-account-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-filter-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-block-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-locker-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("email-setting-efl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.email-misc_worker", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.email-record-video-icon", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.samsungaccount", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.samsungaccount.samsungaccountservice", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.samsungaccount.samsungaccountpushefl", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.samsungaccount.samsungaccountupdate", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.special-day-app", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.special-day-widget", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.tizenstore.billingagent", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.inapppurchase", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.inapppurchase.iapclient", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.inapppurchase.iapservice", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.tizenstore", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.tizenstoreservice", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.videos-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.video-player-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("org.tizen.webcontainer", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.themestore", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("ACL111OMWW.AclService", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("ACL111OMWW.AclManager", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("ACL111OMWW.AclAudioProxyService", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.gallery-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("gallery-efl-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.gallery-lite.appcontrol", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.image-viewer.appcontrol.slideshow", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.image-viewer", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.image-viewer-subapp", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.image-viewer-subapp-single", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("image-viewer-efl-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.gallery-lite.dbox", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.music-player-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.sound-player-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.music-chooser-lite", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.music-player-lite.widget", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
+INSERT INTO restrictions VALUES("com.samsung.cloud-content-sync", 0, 0, 1, 3, 0, 2, "", "seth_w0", "noneimsi");
--- /dev/null
+com.samsung.app-tray
+com.samsung.cluster-home
+com.samsung.data-provider-slave
+com.samsung.lockscreen
+com.samsung.pwlock
+com.samsung.admin-data
+com.samsung.quickpanel
+com.samsung.volume
+com.samsung.indicator
+nas9xepmna.context-service
+popup
+testmode
+wakeup-service
+admin-data
--- /dev/null
+PRAGMA journal_mode = PERSIST;
+PRAGMA user_version = 1;
+
+CREATE TABLE IF NOT EXISTS statistics (
+ binpath TEXT,
+ received BIGINT,
+ sent BIGINT,
+ time_stamp BIGINT,
+ iftype INT,
+ is_roaming INT,
+ hw_net_protocol_type INT,
+ ifname TEXT,
+ 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, imsi);
+
+CREATE TABLE IF NOT EXISTS quotas (
+ binpath TEXT,
+ sent_quota BIGINT,
+ rcv_quota BIGINT,
+ snd_warning_threshold INT,
+ rcv_warning_threshold INT,
+ time_period BIGINT,
+ start_time BIGINT,
+ iftype INT,
+ roaming INT,
+ 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, imsi);
+
+CREATE TABLE IF NOT EXISTS effective_quotas (
+ binpath TEXT,
+ sent_used_quota BIGINT,
+ rcv_used_quota BIGINT,
+ start_time BIGINT,
+ finish_time BIGINT,
+ iftype INT,
+ roaming INT,
+ state INT DEFAULT 0,
+ 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, imsi);
+
+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,
+ imsi TEXT,
+ PRIMARY KEY (binpath, iftype, ifname, quota_id, imsi)
+);
+
+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,
+ 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);
--- /dev/null
+PCL stands for Performance Control Library.
+
+The libresourced library is designed for accounting and limiting CPU, memory and bandwidth usage of all installed applications and daemons. It provides interface for setting limits and quotas and reading statistics.
+
+- CPU and memory usage are tracked and limited using cpu_acct and mem cgroup controllers
+- Network traffic is controlled by special kernel module which works in conjunction with user-space daemon
+
+Network control is performed on 3rd layer of network subsystem using nethooks. To determine which application sent a packet, the netcls cgroup controller is used which tags all outgoing packets.
+There is no direct way to determine to which application an incoming packet belongs. This is solved by comparing destination host/port pairs of incoming packets to known host/port pairs of sent packets.
+There is also a backup way of determining the packet owner by parsing all the tracked applications' /proc entries.
+
+The daemon periodically collects the data from kernel module and stores it into sqlite database.
+
+Communication between daemon and kernel module is done using netlink interface.
--- /dev/null
+Following is the list of functions that are currently exported in libresourced.so
+
+Types:
+======
+
+/**
+ * @brief Datausage quota
+ */
+typedef struct {
+ int time_period;
+ int64_t snd_quota;
+ int64_t rcv_quota;
+ resman_state_t quota_type;
+} resman_datausage_quota;
+
+/**
+ * @brief return type of the counters callback
+ */
+typedef enum {
+ PCL_CANCEL = 0, /**< cancel */
+ PCL_CONTINUE = 1, /**< continue */
+} resman_cb_ret;
+
+/**
+ * @brief callback for enumerate counters and restrictions
+ */
+typedef resman_cb_ret(*resman_perf_info_cb) (const resman_perf_info * info,
+ void *user_data);
+
+/**
+ * @brief Selection rule applied for data usage enumeration
+ */
+typedef struct {
+ time_t from;
+ time_t to;
+ char *iface;
+ int granularity;
+} data_usage_selection_rule;
+
+typedef struct {
+ resman_sql_exec exec;
+} resman_base_query;
+
+typedef resman_ret_c(*resman_sql_exec) (const resman_exec_context *context);
+
+typedef struct {
+ resman_perf_info_cb info_cb;
+ void *user_data;
+ resman_perf_selection_rule *rule;
+} resman_exec_context;
+
+typedef resman_cb_ret(*resman_perf_info_cb) (const resman_perf_info * info,
+ void *user_data);
+/**
+ * @brief Bundle structure for bringing all together application identification
+ * and properties.
+ * 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;
+ const char *iface;
+ resman_tm_interval *interval;
+ resman_common_info foreground;
+ resman_common_info background;
+} resman_perf_info;
+
+/**
+ * @brief Commulative structure for holding data usage information
+ */
+typedef struct {
+ resman_counters cnt;
+ resman_restrictions rst;
+} resman_common_info;
+
+typedef struct {
+ time_t from;
+ time_t to;
+} resman_tm_interval;
+
+resman_ret_c - return value of most functions.
+
+typedef enum {
+ PCL_ERROR_NOTIMPL = -7,
+ PCL_ERROR_UNINITIALIZED = -6,
+ PCL_ERROR_NO_DATA = -5,
+ PCL_ERROR_INVALID_PARAMETER = -4,
+ PCL_ERROR_OUT_OF_MEMORY = -3,
+ PCL_ERROR_DB_FAILED = -2,
+ PCL_ERROR_FAIL = -1,
+ PCL_ERROR_OK = 0
+} resman_ret_c;
+
+/*
+ * cpu_usage: percent of cpu usage
+ * mem_usage: percent of mem usage
+ * incomming_rate_limit: rate limit for incomming packets in bytes per second
+ * outgoing_rate_limit: rate limit for outgoing packets in bytes per second
+ */
+typedef struct {
+ int cpu_usage;
+ int mem_usage;
+ int incoming_rate_limit;
+ int outgoing_rate_limit;
+} resman_restrictions;
+
+/**
+ * @brief Selection rule applied for enumeration
+ * order - order field resman_order_t
+ * filter - fiter field resman_filter
+ * groupping - on what we should groupping our result
+ */
+typedef struct {
+ unsigned char version;
+ u_int32_t order;
+ resman_filter filter;
+ u_int32_t groupping;
+} resman_perf_selection_rule;
+
+Functions:
+==========
+
+int apply_net_restriction(u_int32_t classid, int incoming_rate_limit, int outgoing_rate_limit)
+
+Applies network rate limit to application having the supplied network class ID
+
+classid - network class ID
+incoming_rate_limit - rate limit for incoming traffic
+outgoing_rate_limit - rate limit for outgoing traffic
+
+Return values:
+Non-zero values mean errors while communicating with kernel module
+
+TODO:
+- Currently incoming_rate_limit must be 0, otherwise PCL_ERROR_NOTIMPL will be returned
+
+--------------------------------------------------------------------------------
+resman_ret_c apply_restriction(const char *app_id, const resman_restrictions *foreground, const resman_restrictions *background)
+
+Stores restrictions into database to be applied when application is started again.
+
+app_id - zero-terminated string containing package name of application to be restricted
+foreground - set of restrictions applied to application while in foreground
+background - set of restrictions applied to application while in background
+
+Non-OK return values:
+PCL_ERROR_INVALID_PARAMETER - app_id is NULL
+PCL_ERROR_INVALID_PARAMETER - both foreground and background are NULL
+PCL_ERROR_FAIL - could not determine network class ID for the application
+PCL_ERROR_DB_FAILED - error while storing restrictions to database
+PCL_ERROR_NOTIMPL - incoming_rate_limit is set
+???? - apply_net_restriction can return any non-zero integer value (see above) which will be returned as is
+
+TODO:
+- Does not apply CPU and memory restrictions if application is already running
+- Background settings are ignored
+- Return meaningful result in case apply_net_restriction fails
+
+--------------------------------------------------------------------------------
+resman_ret_c bind_statement(sqlite3_stmt *stm, const resman_perf_selection_rule *rule)
+
+Applies selection rule to the statement.
+
+stm - sqlite statement to bind parameters to
+rule - ???? parameters to be bound to statement
+
+Non-OK return values:
+PCL_ERROR_INVALID_PARAMETER - stm is NULL
+PCL_ERROR_INVALID_PARAMETER - rule filter type is not PCL_FILTER_UNDEF and rule filter value is NULL
+PCL_ERROR_DB_FAILED - database error
+
+TODO:
+The function assumes that bound parameter is always the first parameter to bind. This might break on some statements.
+
+--------------------------------------------------------------------------------
+resman_base_query create_exec(const resman_perf_selection_rule *rule)
+
+Returns a query to be executed based on rule supplied
+
+--------------------------------------------------------------------------------
+int create_netlink(int protocol, int groups)
+
+Create netlink socket.
+
+Results: Created socket on success and -1 on failure.
+
+--------------------------------------------------------------------------------
+resman_ret_c data_usage_details_foreach(const char *app_id,
+ data_usage_selection_rule *rule,
+ resman_perf_info_cb info_cb, void *user_data)
+
+Process data usage details for application on given interval.
+
+app_id - null-terminated string containing package name of the application or NULL for all applications
+rule - parameters of query
+ - from - start of interval
+ - to - end of interval
+ - iface - name of interface or NULL for all interfaces
+ - granularity - split data usage into granularity-sized chunks
+info_cb - pointer to callback to be executed on each record
+user_data - pointer to data which will be passed as argument 3 to callback
+
+Interval is given as a pair of unix timestamps.
+If granularity is not supplied the result is the total data usage on the whole interval. Otherwise there will be one record for each chunk.
+If iface is not supplied there will be one record for each interface.
+If app_id is not supplied then records will contain total amount of data usage by all applications.
+
+info_cb must return PCL_CONTINUE to keep processing results or PCL_CANCEL to stop processing and discard the rest.
+
+Notes:
+Callbacks are issued synchronously. When data_usage_details_foreach returns, all callbacks are guaranteed to have been executed already.
+The function is not thread-safe.
+If granularity is supplied, interval is split into chunks and each record contains traffic during that chunk.
+If interface or granularity is supplied and a record contains no traffic, the record is omitted.
+
+Errors:
+PCL_ERROR_INVALID_PARAMETER - rule or info_cb is NULL
+PCL_ERROR_DB_FAILED - database error
+
+--------------------------------------------------------------------------------
+void data_usage_finalize(void)
+
+Finalizes queries used in data usage functions
+
+--------------------------------------------------------------------------------
+resman_ret_c data_usage_foreach(const data_usage_selection_rule *rule,
+ resman_perf_info_cb info_cb, void *user_data)
+
+Process data usage on given interval.
+
+rule - parameters of query
+ - from - start of interval
+ - to - end of interval
+ - iface - name of interface or NULL for all interfaces
+ - granularity - split data usage into granularity-sized chunks
+info_cb - pointer to callback to be executed on each record
+user_data - pointer to data which will be passed as argument 3 to callback
+
+Interval is given as a pair of unix timestamps. The result contains records for all applications that used network during that interval.
+If granularity is not supplied each record is the total data usage on the whole interval. Otherwise there is a record for each chunk.
+If iface is supplied the result is limited to that interface. Otherwise the result is a total of all interfaces.
+
+info_cb must return PCL_CONTINUE to keep processing results or PCL_CANCEL to stop processing and discard the rest.
+
+Notes:
+Callbacks are issued synchronously. When data_usage_foreach returns, all callbacks are guaranteed to have been executed already.
+The function is not thread-safe.
+If granularity is supplied, interval is split into chunks and each record contains traffic during that chunk. If any record contains no traffic, the record is omitted.
+
+Errors:
+PCL_ERROR_INVALID_PARAMETER - rule or info_cb is NULL
+PCL_ERROR_DB_FAILED - database error
+
+--------------------------------------------------------------------------------
+int data_usage_init(sqlite3 *db)
+
+Initializes queries used in data usage functions.
+
+--------------------------------------------------------------------------------
+void datausage_quota_finalize(void)
+
+Finalizes queries used in data usage quota functions.
+
+--------------------------------------------------------------------------------
+int datausage_quota_init(sqlite3 *db)
+
+Initializes queries used in data usage quota functions.
+
+--------------------------------------------------------------------------------
+u_int32_t get_classid_by_app_id(const char *pkg_name, int create)
+
+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
+
+Returns class ID or 0 in case of error.
+
+--------------------------------------------------------------------------------
+sqlite3 *resourced_get_database(void)
+
+Returns the handler to PCL database containing restrictions and statistics.
+
+--------------------------------------------------------------------------------
+int get_family_id(int sock, pid_t pid)
+
+Probe the controller in genetlink to find the family id for the TRAF_STAT family. (Helper function)
+
+--------------------------------------------------------------------------------
+void get_in_info(int sock, const pid_t pid, const int family_id,
+ in_traffic_event_list **list)
+
+Get list of incoming traffic records from the kernel module.
+
+--------------------------------------------------------------------------------
+void get_out_info(int sock, const pid_t pid,
+ const int family_id, out_traffic_event_list **list)
+
+Get list of outgoing traffic records from the kernel module.
+
+--------------------------------------------------------------------------------
+int make_cgroup_with_pid(char *dpg_name, char *app_path)
+
+Creates a cgroup named dkg_name if needed and place current process to that cgroup.
+
+dpkg_name - name of cgroup
+app_path - used only for debugging
+
+Returns 0 on success -errno on error
+
+Notes:
+The name is misleading, PID is not even accepted as a parameter.
+app_path is used only for debugging and not anywhere in the code
+
+--------------------------------------------------------------------------------
+void notify_daemon(void)
+
+Sends SIGUSR1 to the daemon.
+
+--------------------------------------------------------------------------------
+resman_ret_c resman_perf_info_foreach(const resman_perf_selection_rule *rule,
+ resman_perf_info_cb info_cb, void *user_data)
+
+Processes network usage statistics based on supplied rule.
+
+rule - the rule used for statistics selection
+info_cb - callback performed on each record
+user_data - pointer passed as argument 3 to the callback
+
+Notes:
+Actual behavior depends largely on the rule.
+
+--------------------------------------------------------------------------------
+resman_ret_c resman_sql_rules_exec(const resman_exec_context *context)
+
+Performs the actual query and calls callback on each record.
+
+context - contains the rule and callback
+
+Note:
+Internal function.
+
+--------------------------------------------------------------------------------
+resman_ret_c resman_sql_simple_exec(const resman_exec_context *context)
+
+Performs the basic statistics query and calls callback on each record.
+
+context - contains the rule and callback
+
+Note:
+Internal function.
+
+--------------------------------------------------------------------------------
+void put_attr(rt_param *arg, int type, const void *data, int data_len)
+
+Write attribute to netlink packet. Helper function.
+
+--------------------------------------------------------------------------------
+int receive_answer(int sock, const int attr_type, char **out_buffer, __u16 *arg_count)
+
+Read answer from kernel module. Helper function.
+
+--------------------------------------------------------------------------------
+int revert_net_restriction(u_int32_t classid)
+
+Removes network restrictions set by apply_net_restriction.
+
+classid - network class ID which will be unrestricted
+
+Returns 0 on success, non-zero on failure.
+
+Note:
+Name is a bit misleading in that it removes restrictions, not reverts to previous ones.
+
+--------------------------------------------------------------------------------
+resman_ret_c revert_restriction(const char *app_id)
+
+Removes restrictions set by apply_restriction.
+
+app_id - zero-terminated string containing package name of application.
+
+Returns 0 on success
+PCL_ERROR_DB_FAILED - database error
+???? - any non-zero value could be returned by revert_net_restriction
+
+Notes:
+Does not actually modify CPU or memory limits, only writes new settings to database.
+Name is misleading in that the function removes restrictions, not reverts to previous ones.
+
+--------------------------------------------------------------------------------
+int send_command(int sock, const pid_t pid, const int family_id, __u8 cmd)
+
+Helper function for sending commands to kernel module.
+
+--------------------------------------------------------------------------------
+int send_restriction(int sock, const pid_t pid, const int family_id,
+ const u_int32_t *classids, const u_int16_t classid_count,
+ const enum traffic_restriction_type restriction_type)
+
+Internal function used for setting network restrictions.
+
+--------------------------------------------------------------------------------
+void send_start(int sock, const pid_t pid, const int family_id)
+
+Helper function used in communicating with kernel module.
+
+--------------------------------------------------------------------------------
+resman_ret_c set_datausage_quota(const char *app_id,
+ const resman_datausage_quota *quota)
+
+Sets network traffic quota for application.
+
+app_id - zero-terminated string containing package name of the application.
+quota - network traffic quota to be applied to the application
+
+Returns:
+PCL_ERROR_OK - success
+PCL_ERROR_INVALID_PARAMETER - app_id or quota is NULL
+
+Note:
+Currently only writes quota limits to the database.
+
--- /dev/null
+Table of context
+---------------
+
+1. ERD - Entity relation diagram
+2. Statistics entity
+3. Restrictions entity
+4. Quotas entity
+5. Effective quota entity
+
+
+1. ERD
+
+ +----------------------------+ +--------------------+ +-------------+
+ | restrictions | | statistics | | quotas |
+ |----------------------------| |--------------------| |-------------|
+ |binpath: TEXT |<--->|binpath: TEXT |<-->|binpath |
+ |cpu: INT | | |received: BIGINT | |sent_quota |
+ |mem: INT | | |sent: BIGINT | |rcv_quota |
+ |incomming_rate: INT | | |time_stamp: BIGINT | |time_period |
+ |outgoing_rate: INT | | |ifname: TEXT | |start_time |
+ | | | |ifmac: TEXT | +-------------+
+ | | | | |
+ | | | | |
+ +----------------------------+ | +--------------------+
+ |
+ |
+ | +---------------------------+
+ | | effective_quota |
+ | |---------------------------|
+ +->|binpath |
+ |sent_used_quota |
+ |rcv_used_quota |
+ |start_time |
+ |finish_time |
+ | |
+ +---------------------------+
+
+2. Statistics entity
+--------------------
+Holds information about counted traffic per time points, time_stamp - it's time
+point. Accumulation of the information proceses in main loop of perf-controld.
+
+
+3. Restrictions entity
+----------------------
+Holds information about restriction see set_restriction function. Every time we
+set restriction we store it in the database, it give us an ability to apply
+restriction for example after reboot.
+TODO: Possible bug. We don't separate quota based restriction and user based.
+User based restriction should be applied without conditions, quota based should
+be checked is quota active.
+
+
+4. Quotas entity
+----------------
+Holds information about quota.
+
+
+5. Effective quota entity
+-------------------------
+Holds information about active quota. One enty per quotas time interval.
+
+
+ | time period | time period | time period |
+------------------------------------------------------------------------------>
+ ^ ^ ^
+ start time finish time finish time
+ start time start time
+time line
--- /dev/null
+/*
+ * 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: data_udage.h
+ *
+ * @desc Data usage API
+ * @version 1.0
+ *
+ * Created on: 28 June, 2012
+ */
+
+#ifndef _RESOURCED_DATA_USAGE_H_
+#define _RESOURCED_DATA_USAGE_H_
+
+#include <resourced.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @brief Hardware network protocol types
+ */
+typedef enum {
+ RESOURCED_PROTOCOL_NONE, /**< Network unknown */
+ RESOURCED_PROTOCOL_DATACALL_NOSVC, /**< Network no service */
+ RESOURCED_PROTOCOL_DATACALL_EMERGENCY, /**< Network emergency */
+ RESOURCED_PROTOCOL_DATACALL_SEARCH, /**< Network search 1900 */
+ RESOURCED_PROTOCOL_DATACALL_2G, /**< Network 2G */
+ RESOURCED_PROTOCOL_DATACALL_2_5G, /**< Network 2.5G */
+ RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE, /**< Network EDGE */
+ RESOURCED_PROTOCOL_DATACALL_3G, /**< Network UMTS */
+ RESOURCED_PROTOCOL_DATACALL_HSDPA, /**< Network HSDPA */
+ RESOURCED_PROTOCOL_DATACALL_LTE, /**< Network LTE */
+ RESOURCED_PROTOCOL_MAX_ELEM
+} resourced_hw_net_protocol_type;
+
+/**
+ * @brief State of the monitored process
+ */
+typedef enum {
+ RESOURCED_STATE_UNKNOWN = 0,
+ RESOURCED_STATE_FOREGROUND = 1 << 1, /** < foreground state */
+ RESOURCED_STATE_BACKGROUND = 1 << 2, /** < background state */
+ RESOURCED_STATE_LAST_ELEM = 1 << 3
+} resourced_state_t;
+
+/**
+ * @brief Network restriction states
+ */
+typedef enum {
+ RESOURCED_RESTRICTION_UNKNOWN,
+ RESOURCED_RESTRICTION_ACTIVATED, /** < restriction has been activated */
+ RESOURCED_RESTRICTION_REMOVED, /** < restriction has been removed */
+ RESOURCED_RESTRICTION_EXCLUDED, /** < restriction has been excluded */
+ RESOURCED_RESTRICTION_LAST_ELEM
+} resourced_restriction_state;
+
+/**
+ * @brief Network interface types
+ */
+typedef enum {
+ RESOURCED_IFACE_UNKNOWN, /**< undefined iface */
+ RESOURCED_IFACE_DATACALL, /**< mobile data */
+ RESOURCED_IFACE_WIFI, /**< wifi data */
+ RESOURCED_IFACE_WIRED, /**< wired interface */
+ RESOURCED_IFACE_BLUETOOTH, /**< bluetooth interface */
+ RESOURCED_IFACE_ALL, /**< enumerate all network interface types */
+ RESOURCED_IFACE_LAST_ELEM
+} resourced_iface_type;
+
+/**
+ * @brief Network roaming type
+ */
+typedef enum {
+ RESOURCED_ROAMING_UNKNOWN, /**< can't define roaming - roaming unknown */
+ RESOURCED_ROAMING_ENABLE, /**< in roaming */
+ RESOURCED_ROAMING_DISABLE, /**< not in roaming */
+ RESOURCED_ROAMING_LAST_ELEM,
+} resourced_roaming_type;
+
+/*
+ * 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
+ * roaming - roaming support now only for exclusions for restriction it doesn't
+ * make sense (roaming will be saved as UNKNOWN and restriction will be applied
+ * in any case).
+ *
+ */
+typedef struct {
+ resourced_state_t rs_type;
+ resourced_iface_type iftype;
+ int send_limit;
+ int rcv_limit;
+ int snd_warning_limit;
+ int rcv_warning_limit;
+ resourced_roaming_type roaming;
+ char *ifname;
+ const char *imsi;
+} resourced_net_restrictions;
+
+/**
+ * @brief the same as for restriction
+ */
+typedef struct {
+ long long incoming_bytes;
+ long long outgoing_bytes;
+} resourced_counters;
+
+
+/**
+ * @brief Commulative structure for holding data usage information
+ */
+typedef struct {
+ resourced_counters cnt;
+ resourced_net_restrictions rst;
+} resourced_common_info;
+
+typedef struct {
+ time_t from;
+ time_t to;
+} resourced_tm_interval;
+
+typedef enum {
+ RESOURCED_CON_PERIOD_UNKNOWN, /**< Undefined period */
+ RESOURCED_CON_PERIOD_LAST_RECEIVED_DATA, /**< Last received data */
+ RESOURCED_CON_PERIOD_LAST_SENT_DATA, /**< Last sent data */
+ RESOURCED_CON_PERIOD_TOTAL_RECEIVED_DATA, /**< Total received data */
+ RESOURCED_CON_PERIOD_TOTAL_SENT_DATA, /**< Total sent data */
+ RESOURCED_CON_PERIOD_LAST_ELEM
+} resourced_connection_period_type;
+
+/**
+ * @brief Period used in quota
+ */
+typedef enum {
+ RESOURCED_PERIOD_UNDEF = 0,
+ RESOURCED_PERIOD_HOUR = 3600,
+ RESOURCED_PERIOD_DAY = 86400,
+ RESOURCED_PERIOD_WEEK = 604800,
+ RESOURCED_PERIOD_MONTH = 2419200
+} data_usage_quota_period_t;
+
+/**
+ * @brief Restriction notification warning threshold value
+ * definitions
+ */
+enum {
+ WARNING_THRESHOLD_DEFAULT, /**< for quota it means
+ resourced will evaluate proper value, for restriction it
+ means no warning */
+ WARNING_THRESHOLD_NONE, /**< means no threshold at all */
+};
+
+/**
+ * @brief Datausage quota
+ * time_period - time interval for quota, use predefined quota
+ * @see data_usage_quota_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;
+ 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;
+
+/**
+ * @brief Reset filter for quota
+ * app_id is mandatory field
+ * iftype interface type, RESOURCED_IFACE_UNKNOWN
+ * interface is not valid parameter, use
+ * RESOURCED_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.
+ */
+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;
+};
+
+/**
+ * @brief Selection rule applied for data usage enumeration
+ */
+typedef struct {
+ unsigned char version;
+ time_t from;
+ time_t to;
+ resourced_iface_type iftype;
+ int granularity;
+} data_usage_selection_rule;
+
+/**
+ * @brief Bundle structure for bringing all together application identification
+ * and properties.
+ * 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;
+ const char *ifname;
+ const char *imsi;
+ resourced_iface_type iftype;
+ resourced_tm_interval *interval;
+ resourced_counters cnt;
+ resourced_roaming_type roaming;
+ resourced_hw_net_protocol_type hw_net_protocol_type;
+ resourced_state_t ground;
+} data_usage_info;
+
+/**
+ * @brief callback for enumerate counters and restrictions
+ */
+typedef resourced_cb_ret(*data_usage_info_cb) (const data_usage_info *info,
+ void *user_data);
+
+
+/**
+ * @desc Description of the boolean option for enabling/disabling
+ * network interfaces and enabling/disabling some behaviar
+ */
+typedef enum {
+ RESOURCED_OPTION_UNDEF,
+ RESOURCED_OPTION_ENABLE,
+ RESOURCED_OPTION_DISABLE
+} resourced_option_state;
+
+/**
+ * @desc Set of the options.
+ * version - contains structure version
+ * wifi - enable/disable wifi, RESOURCED_OPTION_UNDEF to leave option as is
+ * datacall - enable/disable datacall, RESOURCED_OPTION_UNDEF to leave option as is
+ * datausage_timer - set period of the updating data from the kernel,
+ * 0 to leave option as is
+ * datacall_logging - enable/disable datacall_logging,
+ * RESOURCED_OPTION_UNDEF to leave option as is
+ */
+typedef struct {
+ unsigned char version;
+ resourced_option_state wifi;
+ resourced_option_state datacall;
+ time_t datausage_timer;
+ resourced_option_state datacall_logging;
+} resourced_net_options;
+
+#define resourced_options resourced_net_options
+
+/**
+ * @brief Structure for information on restrictions.
+ * 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;
+ const char *ifname;
+ resourced_iface_type iftype;
+ resourced_restriction_state rst_state;
+ int rcv_limit;
+ int send_limit;
+ int quota_id;
+ resourced_roaming_type roaming;
+ const char *imsi;
+} resourced_restriction_info;
+
+/**
+ * @brief callback for processing information of restrictions
+ */
+typedef resourced_cb_ret(*resourced_restriction_cb)(
+ const resourced_restriction_info *info, void *user_data);
+
+/**
+ * @desc Reset rule. 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
+ * @see resourced_iface_type, if iftype is RESOURCED_IFACE_LAST_ELEM - erase all
+ * RESOURCED_IFACE_UNKNOW - means undetermined interface
+ * on moment of storing data.
+ * interval - It's time interval, @see resourced_tm_interval. 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;
+ const char *imsi;
+ resourced_iface_type iftype;
+ resourced_tm_interval *interval;
+ resourced_connection_period_type connection_state;
+} data_usage_reset_rule;
+
+resourced_ret_c reset_data_usage(const data_usage_reset_rule *rule);
+
+struct net_activity_info {
+ int type; /*<< ingress/egress */
+ char *appid;
+ int iftype;
+ int bytes;
+};
+
+typedef resourced_cb_ret(*net_activity_cb)(struct net_activity_info *info);
+
+/*
+ * GLOBAL_CONFIG_IMSI is used as a GLOBAL IMSI
+ * for set_net_restriction() and resourced_remove_restriction()
+ */
+#define GLOBAL_CONFIG_IMSI "noneimsi"
+
+/**
+ * @desc Set and apply restriction for application.
+ * It will create new restriction or modify existing.
+ * @param app_id[in] - application identifier, it's package name now
+ * @param restriction[in] - restriction to apply for application
+ * in foreground mode
+ * At least one of the restriction should be setted.
+ * @return 0 on success, otherwise error code
+ */
+resourced_ret_c set_net_restriction(const char *app_id,
+ const resourced_net_restrictions *restriction);
+
+/**
+ * @desc Remove existing restriction for application
+ * It will delete restriction rule in kernel
+ * @param app_id[in] - application identifier, it's package name
+ */
+resourced_ret_c remove_restriction(const char *app_id);
+
+resourced_ret_c remove_restriction_by_iftype(const char *app_id,
+ const resourced_iface_type iftype);
+resourced_ret_c remove_restriction_full(const char *app_id,
+ const resourced_net_restrictions *restriction);
+
+
+/**
+ * @desc Remove existing restriction for application
+ * It will delete restriction rule in kernel
+ * @param app_id[in] - application identifier, it's package name
+ * @param imsi[in] - telephony imsi
+ */
+resourced_ret_c resourced_remove_restriction(const char *app_id, char *imsi);
+
+resourced_ret_c resourced_remove_restriction_by_iftype(const char *app_id,
+ const resourced_iface_type iftype, char *imsi);
+
+/**
+ * @desc Exclude restriction for application
+ * It will exclude restriction rule in kernel
+ * @param app_id[in] - application identifier, it's package name
+ * This function is deprecated, use set_net_exclusion
+ */
+resourced_ret_c exclude_restriction(const char *app_id);
+
+/**
+ * This function is deprecated, use set_net_exclusion
+ */
+resourced_ret_c exclude_restriction_by_iftype(
+ const char *app_id, const resourced_iface_type iftype);
+
+
+/**
+ * @brief Exclude application from network restriction.
+ * Excluded application will be granted to
+ * internet access, in case of whole network restriction.
+ * iftype and roaming in resourced_net_restriction is supported right now
+ */
+resourced_ret_c set_net_exclusion(const char *app_id,
+ const resourced_net_restrictions *rst);
+
+/**
+ * @desc Remove datausage quota by quota rule
+ */
+resourced_ret_c remove_datausage_quota(
+ const struct datausage_quota_reset_rule *rule);
+
+/**
+ * @deprecated
+ */
+resourced_ret_c remove_datausage_quota_by_iftype(
+ const char *app_id, const resourced_iface_type iftype);
+
+/**
+ * @desc Set options, daemon will handle option setting.
+ */
+resourced_ret_c set_resourced_options(const resourced_options *options);
+
+/**
+ * @desc Obtain performance control options.
+ */
+resourced_ret_c get_resourced_options(resourced_options *options);
+
+/**
+ * @desc 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
+ */
+resourced_ret_c set_datausage_quota(const char *app_id,
+ const data_usage_quota *quota);
+
+/**
+ * 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.
+ *
+ * @brief Data usage enumerate function
+ */
+resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb, void *user_data);
+
+/**
+ * The callback is called for each application that restricted now
+ *
+ * @brief Restrictions enumerate function
+ */
+resourced_ret_c restrictions_foreach(resourced_restriction_cb restriction_cb,
+ void *user_data);
+
+/**
+ * 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.
+ *
+ * @brief Data usage details enumerate function
+ */
+resourced_ret_c data_usage_details_foreach(const char *app_id,
+ data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb,
+ void *user_data);
+
+/**
+ * @desc This function registering callback which invokes per every packet.
+ * Function creates new reading thread and returns.
+ */
+resourced_ret_c register_net_activity_cb(net_activity_cb activity_cb);
+
+/**
+ * @desc This function updates the resourced counters and stores in the database
+ */
+resourced_ret_c resourced_update_statistics(void);
+
+resourced_ret_c get_restriction_state(const char *pkg_id,
+ resourced_iface_type iftype, resourced_restriction_state *state);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RESOURCED_DATA_USAGE_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Library for getting process statistics
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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 __LIB_PROC_STAT__
+#define __LIB_PROC_STAT__
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif /* !__cplusplus */
+
+#include <glib.h>
+#include <resourced.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+typedef struct proc_stat_process_info /** process statistics */
+{
+ pid_t pid; /**< the process ID */
+ char name[NAME_MAX]; /**< the name of process */
+
+ bool valid; /** < whether this information is valid (can be used)*/
+ bool active; /**< whether this process is active */
+ bool fresh; /**< whether this process is newly created */
+ unsigned long utime_diff; /**< user mode time this process spent during this measurement interval */
+ unsigned long stime_diff; /**< kernel mode time this process spent during this measurement interval */
+ unsigned long utime_prev; /**< previous user mode time this process spent during the last measurement interval */
+ unsigned long stime_prev; /**< previous kernel mode time this process spent during the last measurement interval */
+
+ unsigned int rss; /**<resident set size of this process by Kb */
+
+} proc_stat_process_info;
+
+
+typedef struct proc_stat_system_time /** The time information system spent, measured in units of USER_HZ **/
+{
+ unsigned long long total_time; /**< the total time system spent */
+ unsigned long long user_time; /**< the time system spent in user mode */
+ unsigned long long nice_time; /**< the time system spent in user mode with low priority(nice) */
+ unsigned long long system_time; /**< the time system spent in system mode */
+ unsigned long long idle_time; /**< the time system spent in idle task */
+ unsigned long long iowait_time; /**< the time system spent waiting for IO */
+ unsigned long long irq_time; /**< the time system spent servicing interrupts */
+ unsigned long long softirq_time; /**< the time system spent servicing softirqs */
+} proc_stat_system_time;
+
+
+/**
+* The following APIs are not thread safe !!!
+*
+*/
+
+/**
+ * @brief Initialize internal resources which are used for managing process statistics
+ *
+ * @return nothing
+ *
+ * This function initializes internal resources which are used for managing process statistics so should be called firstly
+ */
+void proc_stat_init(void);
+
+/**
+ * @brief Release internal resources which are used for managing process statistics
+ *
+ * @return nothing
+ *
+ * This function releases internal resources which are used for managing process statistics
+ */
+
+void proc_stat_finalize(void);
+
+
+/**
+ * @brief Get process statistics between two consecutive its calls
+ *
+ * @param valid_proc_infos GArray instance to be filled with valid proc_stat_process_info
+ * @param terminated_proc_infos GArray instance to be filled with proc_stat_process_info instances which were terminated between two consecutive its calls
+ ,pass NULL if this information is not necessary
+ * @param total_valid_proc_time the sum of time spent by all valid proc_stat_process_info instance, pass NULL if if this information is not necessary
+ * @return true on success.
+ *
+ * This function gets process statistics between two consecutive its calls
+ */
+
+bool proc_stat_get_process_info(GArray *valid_proc_infos, GArray *terminated_proc_infos,
+ unsigned long *total_valid_proc_time);
+
+/**
+ * @brief Get the difference of system time between two consecutive its calls
+ *
+ * @param st_diff the difference of system time
+ * @return true on success, false when it is called first because it can't get the time difference.
+ *
+ * This function gets the difference of system time between two consecutive its calls
+ */
+bool proc_stat_get_system_time_diff(proc_stat_system_time *st_diff);
+
+
+/**
+ * @brief get total memory size by MB unit from /proc/meminfo
+ *
+ * @param total_mem to get the value of MemTotal
+ * @return true on success, false when it doesn't get values from /proc/meminfo
+ *
+ * This function gets total memory size by MB unit from /proc/meminfo
+ * total is from "MemTotal"
+ */
+
+bool proc_stat_get_total_mem_size(unsigned int *total_mem);
+
+/**
+ * @brief get free memory size by MB unit from /proc/meminfo
+ *
+ * @param free_mem to get free size of memory
+ * @return true on success, false when it doesn't get values from /proc/meminfo
+ *
+ * This function gets free memory size by MB unit from /proc/meminfo
+ * free_mem is calculated by "MemFree" + "Buffers" + "Cached" + "SwapCache" - "Shmem"
+ */
+
+bool proc_stat_get_free_mem_size(unsigned int *free_mem);
+
+/**
+ * @brief get CPU time by pid
+ *
+ * @param pid which process to get CPU time
+ * @param utime user mode time this process spent
+ * @param stime kernel mode time this process spent
+ * @return true on success, false when it doesn't get values from /proc/<pid>/stat
+ *
+ * This function gets CPU usage of a process by clock ticks unit from /proc/<pid>/stat
+ */
+
+bool proc_stat_get_cpu_time_by_pid(pid_t pid, unsigned long *utime, unsigned long *stime);
+
+/**
+ * @brief get memory usage by pid
+ *
+ * @param pid which process to get memory usage
+ * @param rss a process's memory usage
+ * @return true on success, false when it doesn't get values from /proc/<pid>/statm
+ *
+ * This function gets memory usage of a process by KB unit from rss of /proc/<pid>/statm
+ */
+
+bool proc_stat_get_mem_usage_by_pid(pid_t pid, unsigned int *rss);
+
+
+
+/**
+ * @brief Get process name
+ *
+ * @param pid which process to get name
+ * @name name a process's name
+ * the size of name should be equal or larger than NAME_MAX
+ * @return true on success, false on failure.
+ *
+ * This function gets process name
+ *
+ */
+bool proc_stat_get_name_by_pid(pid_t pid, char *name);
+
+
+
+/**
+ * @brief Get pids under /proc file system
+ *
+ * @param pids which is filled with pids under /proc file system
+ * The memory to accommodate pids will be allocated in this fuction
+ * so the caller has reponsibility to free this memory
+ * @param cnt which is the count of pids
+ * @return true on success, false on failure.
+ *
+ * This function fills pids(param) with pids under /proc file system.
+ *
+ */
+bool proc_stat_get_pids(pid_t **pids, int *cnt);
+
+
+/**
+ * @brief return whether currently GPU is on or off
+ *
+ * @return true on GPU being on, false on GPU being off
+ *
+ * This function returns whether currently GPU is on or off
+ *
+ */
+bool proc_stat_is_gpu_on(void);
+
+
+/**
+ * @brief return GPU clock by MHz unit
+ *
+ * @return return GPU clock on success , -1 on false
+ *
+ * This function returns GPU clock
+ *
+ */
+
+unsigned int proc_stat_get_gpu_clock(void);
+
+
+enum proc_cgroup_cmd_type { /** cgroup command type **/
+ PROC_CGROUP_SET_FOREGRD,
+ PROC_CGROUP_SET_ACTIVE,
+ PROC_CGROUP_SET_BACKGRD,
+ PROC_CGROUP_SET_INACTIVE,
+ PROC_CGROUP_SET_LAUNCH_REQUEST,
+ PROC_CGROUP_SET_RESUME_REQUEST,
+ PROC_CGROUP_SET_TERMINATE_REQUEST,
+ PROC_CGROUP_SET_SERVICE_REQUEST,
+ PROC_CGROUP_SET_NOTI_REQUEST,
+ PROC_CGROUP_SET_PROC_EXCLUDE_REQUEST,
+ PROC_CGROUP_GET_MEMSWEEP,
+ PROC_CGROUP_SET_TERMINATED,
+ PROC_CGROUP_SET_SYSTEM_SERVICE,
+ PROC_CGROUP_GET_CMDLINE,
+ PROC_CGROUP_GET_EXE,
+ PROC_CGROUP_GET_STAT,
+ PROC_CGROUP_GET_STATUS,
+ PROC_CGROUP_GET_OOMSCORE,
+ PROC_CGROUP_GET_PGID_CMDLINE,
+};
+
+
+/**
+ * @desc Set processes to foreground.
+ */
+resourced_ret_c proc_cgroup_foregrd(void);
+
+/**
+ * @desc Set processes to background.
+ */
+resourced_ret_c proc_cgroup_backgrd(void);
+
+/**
+ * @desc Set process to active
+ */
+resourced_ret_c proc_cgroup_active(pid_t pid);
+
+/**
+ * @desc Set process to inactive
+ */
+resourced_ret_c proc_cgroup_inactive(pid_t pid);
+
+/**
+ * @desc Change process status about cgroup with type
+ */
+resourced_ret_c proc_group_change_status(int type, pid_t pid, char* app_id);
+
+/**
+ * @desc Send process launch request
+ */
+resourced_ret_c proc_cgroup_launch(int type, pid_t pid, char* app_id, char* pkg_id);
+
+/**
+ * @brief get proc filesystem component
+ *
+ * @return return get proc filesystem on success , -1 on false
+ * @param type component of proc filesystem
+ * @param pid process id
+ * @param buf data buffer
+ * @param len read length
+ *
+ */
+resourced_ret_c proc_stat_get_pid_entry(int type, pid_t pid, char* buf, int len);
+
+/**
+ * @brief sweep memory about background processes
+ *
+ * @return return num of swept processes
+ *
+ * This function returns GPU clock
+ *
+ */
+resourced_ret_c proc_cgroup_sweep_memory(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __LIB_PROC_STAT__ */
--- /dev/null
+/*
+ * 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: resourced.h
+ *
+ * @desc Performance management API
+ * @version 2.0
+ *
+ * Created on: May 30, 2012
+ */
+
+#ifndef _SYSTEM_RESOURCE_RESOURCED_H_
+#define _SYSTEM_RESOURCE_RESOURCED_H_
+
+#include <sys/types.h>
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif /* !__cplusplus */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define RESOURCED_ALL_APP "RESOURCED_ALL_APPLICATION_IDENTIFIER"
+#define TETHERING_APP_NAME "RESOURCED_TETHERING_APPLICATION_IDENTIFIER"
+
+
+/**
+ * @brief return code of the rsml's function
+ */
+typedef enum {
+ RESOURCED_ERROR_OOM = -10, /**< Out of memory error, allocation failed */
+ RESOURCED_ERROR_NONMONITOR = -9, /** < Process don't show watchdog popup */
+ RESOURCED_ERROR_NONFREEZABLE = -8, /** < Process is nonfrizable */
+ RESOURCED_ERROR_NOTIMPL = -7, /**< Not implemented yet error */
+ RESOURCED_ERROR_UNINITIALIZED = -6, /**< Cgroup doen't
+ mounted or daemon not started */
+ RESOURCED_ERROR_NO_DATA = -5, /**< Success, but no data */
+ RESOURCED_ERROR_INVALID_PARAMETER = -4,/**< Invalid parameter */
+ RESOURCED_ERROR_OUT_OF_MEMORY = -3, /**< DEPRECATED: Out of memory */
+ RESOURCED_ERROR_DB_FAILED = -2, /**< Database error */
+ RESOURCED_ERROR_FAIL = -1, /**< General error */
+ RESOURCED_ERROR_NONE = 0 /**< General success */
+} resourced_ret_c;
+
+#define RESOURCED_ERROR_OK RESOURCED_ERROR_NONE
+
+/**
+ * @brief return type of the counters callback
+ */
+typedef enum {
+ RESOURCED_CANCEL = 0, /**< cancel */
+ RESOURCED_CONTINUE = 1, /**< continue */
+} resourced_cb_ret;
+
+/**
+ * @desc 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.
+ * @param app_id[in] - application identifier, it's package name now
+ * @param pid - pid to put in to cgroup, or self pid of 0
+ * @return 0 if success or error code
+ */
+resourced_ret_c join_app_performance(const char *app_id, const pid_t pid);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _SYSTEM_RESOURCE_RESOURCED_H_ */
--- /dev/null
+<manifest>
+<request>
+ <domain name="_"/>
+</request>
+</manifest>
--- /dev/null
+# Package Information for pkg-config
+#
+# Copyright (c) 2014 Samsung Electronics Co., Ltd.
+# All rights reserved.
+#
+
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: @PC_NAME@
+Description: Tizen Resource management library
+Version: @VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_PROVIDED_LIBS@
+Cflags: @PC_CFLAGS@
--- /dev/null
+diff --git a/CMakeLists/resourced.txt b/CMakeLists/resourced.txt
+index f0b6f36..856c387 100644
+--- a/CMakeLists/resourced.txt
++++ b/CMakeLists/resourced.txt
+@@ -108,3 +108,17 @@ INSTALL(FILES ${DATA_DIR}/resourced
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE WORLD_READ)
+ INSTALL(FILES ${DATA_DIR}/traffic_db.sql
+ DESTINATION /usr/share)
++
++IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
++ SET(NET_ACTIVITY_PROJECT test_net_activity)
++ ADD_EXECUTABLE(${NET_ACTIVITY_PROJECT}
++ ${UTILS_SOURCE_DIR}/test-net-activity.c)
++ TARGET_LINK_LIBRARIES(${NET_ACTIVITY_PROJECT} ${daemon_pkgs_LDFLAGS}
++ resourced)
++ INSTALL(FILES ${NET_ACTIVITY_PROJECT}
++ DESTINATION ${MAKE_INSTALL_PREFIX}/usr/bin
++ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE WORLD_EXECUTE)
++
++ENDIF()
++
++
+
+diff --git a/packaging/system-resource.spec b/packaging/system-resource.spec
+index cad7917..bc90f26 100644
+--- a/packaging/system-resource.spec
++++ b/packaging/system-resource.spec
+@@ -73,7 +73,7 @@ echo "\
+ #define MAJOR_VERSION ${MAJORVER}
+ #define PATCH_VERSION ${PATCHVER}" > src/common/version.h
+
+-cmake . -DCMAKE_INSTALL_PREFIX=/usr -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DCMAKE_BUILD_TYPE=Release \
++cmake . -DCMAKE_INSTALL_PREFIX=/usr -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DCMAKE_BUILD_TYPE=DEBUG \
+ -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}
+@@ -149,6 +149,7 @@ touch %{exclude_list_opt_full_path}
+ %config(noreplace) %attr(660,root,app) %{database_full_path}
+ %config(noreplace) %attr(660,root,app) %{database_full_path}-journal
+ /usr/bin/datausagetool
++/usr/bin/test_net_activity
+ %config %{_sysconfdir}/dbus-1/system.d/resourced.conf
+ %{_libdir}/systemd/system/resourced.service
+ %{_libdir}/systemd/system/multi-user.target.wants/resourced.service
--- /dev/null
+[Unit]
+Description=make cpucgroup
+After=graphical.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/resourced-cpucgroup.sh
+
+[Install]
+WantedBy=graphical.target
--- /dev/null
+Name: resourced
+Summary: Resource management daemon
+Version: 0.2.87
+Release: 0
+Group: System/Libraries
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source2: resourced-cpucgroup.service
+
+%define cpu_module ON
+%define vip_agent_module ON
+%define timer_slack OFF
+
+%define heart_module ON
+
+%define memory_module ON
+%define block_module ON
+%define wearable_noti OFF
+%define network_state OFF
+%define memory_eng ON
+
+%define slp_tests OFF
+
+%if "%{?tizen_profile_name}" == "mobile"
+ %define swap_module ON
+ %define freezer_module ON
+ %define network_state OFF
+ %define tethering_feature OFF
+ %define wearable_noti OFF
+ %define telephony_feature OFF
+%endif
+
+%if "%{?tizen_profile_name}" == "wearable"
+ %define freezer_module ON
+ %define swap_module ON
+ %define network_state OFF
+ %define tethering_feature OFF
+ %define wearable_noti ON
+ %define telephony_feature OFF
+%endif
+
+%if "%{?tizen_profile_name}" == "tv"
+ %define freezer_module OFF
+ %define swap_module OFF
+ %define network_state OFF
+ %define tethering_feature OFF
+ %define wearable_noti OFF
+ %define telephony_feature OFF
+%endif
+
+%define exclude_list_file_name resourced_proc_exclude.ini
+%define exclude_list_full_path /usr/etc/%{exclude_list_file_name}
+%define exclude_list_opt_full_path /opt/usr/etc/%{exclude_list_file_name}
+%define database_full_path /opt/usr/dbspace/.resourced-datausage.db
+
+%define logging_db_full_path /opt/usr/dbspace/.resourced-logging.db
+%define logging_storage_db_full_path /opt/usr/dbspace/.resourced-logging-storage.db
+
+BuildRequires: cmake
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(sqlite3)
+BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(vconf-internal-keys)
+BuildRequires: pkgconfig(ecore)
+BuildRequires: pkgconfig(ecore-file)
+BuildRequires: pkgconfig(eina)
+BuildRequires: pkgconfig(edbus)
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(openssl)
+BuildRequires: pkgconfig(leveldb)
+BuildRequires: pkgconfig(eventsystem)
+BuildRequires: pkgconfig(capi-system-info)
+
+#only for data types
+BuildRequires: pkgconfig(tapi)
+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+BuildRequires: gperf
+
+%description
+Resourced daemon
+
+%package resourced
+Summary: Resource Daemon
+Group: System/Libraries
+
+%description resourced
+Resource management daemon for memory management and process management (vip processes)
+
+%package -n libresourced
+Summary: Resource Daemon Library
+Group: System/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description -n libresourced
+Library for resourced (Resource Management Daemon)
+
+%package -n libresourced-devel
+Summary: Resource Daemon Library (Development)
+Group: System/Libraries
+Requires: libresourced = %{version}-%{release}
+
+%description -n libresourced-devel
+Library (development) for resourced (Resource Management Daemon)
+
+%if %{?slp_tests} == ON
+%package -n resourced-test
+Summary: Resource test tools
+Group: System/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description -n resourced-test
+This package include set of test programs
+%endif
+
+%prep
+%setup -q
+
+%build
+MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
+MINORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $2}'`
+PATCHVER=`echo %{version} | awk 'BEGIN {FS="."}{print $3}'`
+echo "\
+/* That file was generated automaticaly. Don't edit it */
+#define MINOR_VERSION ${MINORVER}
+#define MAJOR_VERSION ${MAJORVER}
+#define PATCH_VERSION ${PATCHVER}" > src/common/version.h
+
+export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
+export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
+
+%ifarch %{arm}
+%define ARCH arm
+%else
+%define ARCH i586
+%endif
+
+%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+ -DFULLVER=%{version} \
+ -DMAJORVER=${MAJORVER} \
+ -DARCH=%{ARCH} \
+ -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} \
+ -DFREEZER_MODULE=%{freezer_module} \
+ -DCPU_MODULE=%{cpu_module} \
+ -DMEMORY_ENG=%{memory_eng} \
+ -DVIP_AGENT=%{vip_agent_module} \
+ -DTELEPHONY_FEATURE=%{telephony_feature} \
+ -DTIMER_SLACK=%{timer_slack} \
+ -DHEART_MODULE=%{heart_module} \
+ -DDATAUSAGE_TYPE=NFACCT \
+ -DMEMORY_MODULE=%{memory_module} \
+ -DWEARABLE_NOTI=%{wearable_noti} \
+ -DBLOCK_MODULE=%{block_module} \
+ -DSLP_TESTS=%{slp_tests}
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}/usr/share/license
+cp -f LICENSE %{buildroot}/usr/share/license/%{name}
+cp -f LICENSE %{buildroot}/usr/share/license/libresourced
+
+%make_install
+%if %{?heart_module} == ON
+ mkdir -p %{buildroot}/opt/usr/data/heart
+ mkdir -p %{buildroot}/opt/usr/dbspace
+ sqlite3 %{buildroot}%{logging_db_full_path}
+ sqlite3 --line %{buildroot}%{logging_storage_db_full_path} 'PRAGMA journal_mode = WAL'
+ touch %{buildroot}%{logging_storage_db_full_path}-shm
+ touch %{buildroot}%{logging_storage_db_full_path}-wal
+%endif
+
+%if %{?network_state} == ON
+ mkdir -p %{buildroot}/opt/usr/dbspace
+ sqlite3 %{buildroot}%{database_full_path} < %{buildroot}/usr/share/traffic_db.sql
+ rm %{buildroot}/usr/share/traffic_db.sql
+ sqlite3 %{buildroot}%{database_full_path} < %{buildroot}/usr/share/exception_db.sql
+ rm %{buildroot}/usr/share/exception_db.sql
+%endif
+
+mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants
+
+%if %{?cpu_module} == OFF
+mkdir -p %{buildroot}%{_libdir}/systemd/system/graphical.target.wants
+install -m 0644 %SOURCE6 %{buildroot}%{_libdir}/systemd/system/resourced-cpucgroup.service
+ln -s ../resourced-cpucgroup.service %{buildroot}%{_libdir}/systemd/system/graphical.target.wants/resourced-cpucgroup.service
+%endif
+
+%pre resourced
+if [ "$1" = "2" ]; then # upgrade begins
+ systemctl stop resourced.service
+fi
+
+%post
+
+/sbin/ldconfig
+
+mkdir -p %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
+ln -sf %{_libdir}/systemd/system/resourced.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
+
+#install init.d script
+mkdir -p /opt/usr/etc
+#make empty dynamic exclude list for first installation
+touch %{exclude_list_opt_full_path}
+
+if [ "$1" = "2" ]; then # upgrade begins
+ systemctl start resourced.service
+fi
+
+%files -n resourced
+/usr/share/license/%{name}
+/etc/smack/accesses2.d/resourced.rule
+%attr(-,root, root) %{_bindir}/resourced
+%manifest resourced.manifest
+%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
+ %{_bindir}/net-cls-release
+%endif
+%config %{_sysconfdir}/dbus-1/system.d/resourced.conf
+%{_libdir}/systemd/system/resourced.service
+%{_libdir}/systemd/system/multi-user.target.wants/resourced.service
+%{_libdir}/systemd/system/resourced.socket
+%{_libdir}/systemd/system/sockets.target.wants/resourced.socket
+%config /etc/resourced/memory.conf
+%config /etc/resourced/proc.conf
+%if %{?cpu_module} == ON
+ %config /etc/resourced/cpu.conf
+%else
+ %{_bindir}/resourced-cpucgroup.sh
+ %{_libdir}/systemd/system/resourced-cpucgroup.service
+ %{_libdir}/systemd/system/graphical.target.wants/resourced-cpucgroup.service
+%endif
+%if %{?swap_module} == ON
+ %config /etc/resourced/swap.conf
+%endif
+%if %{?vip_agent_module} == ON
+ %config /etc/resourced/vip-process.conf
+ %attr(-,root, root) %{_bindir}/vip-release-agent
+%endif
+%if %{?timer_slack} == ON
+ %config /etc/resourced/timer-slack.conf
+%endif
+%if %{?block_module} == ON
+ %config /etc/resourced/block.conf
+%endif
+%if %{?freezer_module} == ON
+ %{_libdir}/libsystem-freezer.so*
+ %config /etc/resourced/freezer.conf
+%endif
+%{exclude_list_full_path}
+%if %{?heart_module} == ON
+ %config /etc/resourced/heart.conf
+ %attr(700, root, root) /opt/etc/dump.d/module.d/dump_heart_data.sh
+ %attr(700, app, app) %{logging_storage_db_full_path}
+ %attr(700, app, app) %{logging_storage_db_full_path}-shm
+ %attr(700, app, app) %{logging_storage_db_full_path}-wal
+%endif
+%if %{?slp_tests} == ON
+ /usr/bin/resourced-test
+ /usr/lib/systemd/system/resourced-test.service
+ /usr/share/dbus-1/system-services/org.tizen.system.resourced-test.service
+%endif
+#memps
+%attr(-,root, system) %{_bindir}/memps
+#mem-stress
+%attr(-,root, root) %{_bindir}/mem-stress
+%{_libdir}/systemd/system/mem-stress.service
+%{_libdir}/systemd/system/graphical.target.wants/mem-stress.service
+
+%files -n libresourced
+%manifest libresourced.manifest
+%defattr(-,root,root,-)
+/usr/share/license/libresourced
+#proc-stat part
+%{_libdir}/libproc-stat.so.*
+#network part
+%{_libdir}/libresourced.so*
+
+%files -n libresourced-devel
+%defattr(-,root,root,-)
+%{_libdir}/pkgconfig/*.pc
+%{_includedir}/system/resourced.h
+#proc-stat part
+%{_includedir}/system/proc_stat.h
+%{_libdir}/libproc-stat.so
+#network part
+%{_libdir}/libresourced.so
+%{_includedir}/system/data_usage.h
+
+%if %{?slp_tests} == ON
+%files -n resourced-test
+%defattr(-,root,root,-)
+%{_libdir}/resourced/test/test-file-helper
+%{_libdir}/resourced/test/test-smaps
+%{_libdir}/resourced/test/test-procfs
+%{_bindir}/sluggish-test
+%config /etc/resourced/sluggish-test.conf
+%endif
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy context="default">
+ <deny own="org.tizen.resourced"/>
+ </policy>
+ <policy user="root">
+ <allow own="org.tizen.resourced"/>
+ </policy>
+</busconfig>
--- /dev/null
+<manifest>
+ <define>
+ <domain name="resourced"/>
+ <provide>
+ <label name="resourced::db"/>
+ <label name="resourced::storage::db"/>
+ <label name="resourced::storage::wal"/>
+ <label name="resourced::systeminfo"/>
+ </provide>
+ <request>
+ <smack request="resourced::systeminfo" type="rw"/>
+ </request>
+ </define>
+ <assign>
+ <filesystem path="/usr/bin/memps" label="memps" exec_label="^" />
+ <filesystem path="/opt/usr/dbspace/.resourced-datausage.db*" label="resourced::db"/>
+ <filesystem path="/opt/usr/dbspace/.resourced-logging-storage.db" label="resourced::storage::db"/>
+ <filesystem path="/opt/usr/dbspace/.resourced-logging-storage.db-shm" label="resourced::storage::wal"/>
+ <filesystem path="/opt/usr/dbspace/.resourced-logging-storage.db-wal" label="resourced::storage::wal"/>
+ <filesystem path="/etc/dbus-1/system.d/resourced.conf" label="_"/>
+ <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>
--- /dev/null
+resourced system-apps r
+resourced sys-assert::core rwxat
+resourced system::media rwxat
+resourced system::media::root rwxat
+resourced system::sys_logging 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
+resourced telephony_framework::properties rw
+resourced tizen::vconf::public::r::platform::rw rw
+resourced tizen::vconf::network r
+resourced procps arwx
+resourced memps arwx
--- /dev/null
+#!/bin/sh
+
+CPU_CGROUP="/sys/fs/cgroup/cpu/background"
+CPU_SHARE="50"
+CPU_CONTROL_LIST="indicator net-config"
+
+/bin/mkdir -pm 755 $CPU_CGROUP
+echo $CPU_SHARE > $CPU_CGROUP/cpu.shares
+for list in $CPU_CONTROL_LIST; do
+ pid=`/usr/bin/pgrep $list`
+ if [ "z${pid}" != "z" ]; then
+ echo $pid > $CPU_CGROUP/cgroup.procs
+ fi
+done
+
--- /dev/null
+CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
+
+#CMake doesnt' support same project name for several TARGETS
+# libresourced uses it
+PROJECT(resource_d)
+
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/InstallSymlink.cmake)
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ProcessGPERF.cmake)
+
+PROCESS_GPERF(${COMMON_SOURCE_DIR}/smaps-lookup.gperf ${COMMON_SOURCE_DIR}/smaps-lookup.c)
+PROCESS_GPERF(${COMMON_SOURCE_DIR}/meminfo-lookup.gperf ${COMMON_SOURCE_DIR}/meminfo-lookup.c)
+SET(RESOURCED_SHARED_SOURCES ${RESOURCED_SHARED_SOURCES}
+ ${COMMON_SOURCE_DIR}/smaps-lookup.c
+ ${COMMON_SOURCE_DIR}/meminfo-lookup.c
+ )
+
+SET(CMAKE_EXTRA_INCLUDE_FILES unistd.h)
+
+INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR}
+ ${RESOURCED_SOURCE_DIR}
+ ${HEART_SOURCE_DIR}/include
+ ${MEMORY_SOURCE_DIR}
+ ${PROC-STAT_SOURCE_DIR}/include
+ ${APP-STAT_SOURCE_DIR}/include
+ ${NETWORK_SOURCE_DIR}/include
+ ${BLOCK_SOURCE_DIR}/include
+ ${RESOURCED_DBUS_SOURCE_DIR}/include
+ ${FREEZER_SOURCE_DIR}/include
+)
+
+SET(SOURCES
+ ${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
+ ${PROC-STAT_SOURCE_DIR}/proc-appusage.c
+ ${PROC-STAT_SOURCE_DIR}/proc-info.c
+ ${SLUGGISH_SOURCE_DIR}/sluggish.c
+ ${RESOURCED_SOURCE_DIR}/init.c
+ ${RESOURCED_SOURCE_DIR}/main.c
+ ${COMMON_SOURCE_DIR}/procfs.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}
+ ${NETWORK_SOURCE_DIR}/nfacct-rule.c
+ ${NETWORK_SOURCE_DIR}/nf-restriction.c
+ )
+ ELSE()
+ 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()
+
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/counter-process.c
+ ${NETWORK_SOURCE_DIR}/options-private.c
+ ${NETWORK_SOURCE_DIR}/datausage-quota-processing.c
+ ${NETWORK_SOURCE_DIR}/datausage-vconf-callbacks.c
+ ${NETWORK_SOURCE_DIR}/datausage-vconf-common.c
+ ${NETWORK_SOURCE_DIR}/specific-trace.c
+ ${NETWORK_SOURCE_DIR}/datausage-common.c
+ ${NETWORK_SOURCE_DIR}/iface-cb.c
+ ${NETWORK_SOURCE_DIR}/nl-helper.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
+ )
+
+ 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()
+
+
+#freezer module
+IF("${FREEZER_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES} ${FREEZER_SOURCE_DIR}/freezer.c)
+ELSE()
+ SET(SOURCES ${SOURCES} ${FREEZER_SOURCE_DIR}/freezer-common.c)
+ENDIF()
+
+#resourced DBus module
+SET(SOURCES ${SOURCES}
+ ${RESOURCED_DBUS_SOURCE_DIR}/resourced-dbus.c
+ )
+
+IF("${VIP_AGENT}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${VIP_SOURCE_DIR}/vip-process.c
+ )
+ENDIF()
+
+#heart module
+IF("${HEART_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${HEART_SOURCE_DIR}/logging.c
+ ${HEART_SOURCE_DIR}/heart.c
+ ${HEART_SOURCE_DIR}/heart-abnormal.c
+ ${HEART_SOURCE_DIR}/heart-appopt.c
+ ${HEART_SOURCE_DIR}/heart-cpu.c
+ ${HEART_SOURCE_DIR}/heart-memory.c
+ ${HEART_SOURCE_DIR}/heart-battery.c
+ ${HEART_SOURCE_DIR}/heart-storage.c
+ ${HEART_SOURCE_DIR}/decision.c
+ ${HEART_SOURCE_DIR}/decision-memory.c
+ )
+ ADD_DEFINITIONS("-DHEART_SUPPORT")
+ENDIF()
+
+#memory module
+IF("${MEMORY_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${MEMORY_SOURCE_DIR}/lowmem-dbus.c
+ ${MEMORY_SOURCE_DIR}/memcontrol.c
+ ${MEMORY_SOURCE_DIR}/vmpressure-lowmem-handler.c
+ )
+ ADD_DEFINITIONS("-DMEMORY_SUPPORT")
+ENDIF()
+
+#swap module
+IF("${SWAP_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${SWAP_SOURCE_DIR}/swap.c
+ )
+ ADD_DEFINITIONS("-DSWAP_SUPPORT")
+ENDIF()
+
+IF("${CPU_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${CPU_SOURCE_DIR}/cpu.c
+ )
+ENDIF()
+
+IF("${TIMER_SLACK}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${TIMER_SOURCE_DIR}/timer-slack.c
+ )
+ENDIF()
+
+IF("${BLOCK_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${BLOCK_SOURCE_DIR}/block.c
+ ${BLOCK_SOURCE_DIR}/block-monitor.c
+ )
+ENDIF()
+
+SET(REQUIRES_LIST ${REQUIRES_LIST}
+ ecore
+ dlog
+ glib-2.0
+ sqlite3
+ vconf
+ vconf-internal-keys
+ ecore-file
+ eina
+ edbus
+ eina
+ libsystemd
+ leveldb
+ eventsystem
+ )
+
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(RESOURCED_REQUIRE_PKGS REQUIRED ${REQUIRES_LIST})
+
+FOREACH(flag ${RESOURCED_REQUIRE_PKGS_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -pthread -fPIE")
+
+CONFIGURE_FILE(${INCLUDE_COMMON_DIR}/config.h.in
+ ${INCLUDE_COMMON_DIR}/config.h)
+
+# resourced shared static lib
+FILE(GLOB FILES "${INCLUDE_COMMON_DIR}/*.h")
+FOREACH(FILE ${FILES})
+ SET(RESOURCED_INCLUDE_HEADERS ${RESOURCED_INCLUDE_HEADERS} ${FILE})
+ENDFOREACH()
+
+SET(RESOURCED_INCLUDE_HEADERS ${RESOURCED_INCLUDE_HEADERS}
+ ${NETWORK_SOURCE_DIR}/include/datausage-vconf-callbacks.h
+ ${NETWORK_SOURCE_DIR}/include/iface-cb.h
+ )
+
+FILE(GLOB FILES "${COMMON_SOURCE_DIR}/*.c")
+FOREACH(FILE ${FILES})
+ SET(RESOURCED_SHARED_SOURCES ${RESOURCED_SHARED_SOURCES} ${FILE})
+ENDFOREACH()
+
+FILE(GLOB FILES "${COMMON_SOURCE_DIR}/*.h")
+FOREACH(FILE ${FILES})
+ SET(RESOURCED_SHARED_HEADERS ${RESOURCED_SHARED_HEADERS} ${FILE})
+ENDFOREACH()
+
+ADD_LIBRARY(resourced_shared STATIC
+ ${RESOURCED_INCLUDE_HEADERS}
+ ${RESOURCED_SHARED_SOURCES}
+ ${RESOURCED_SHARED_HEADERS}
+ )
+TARGET_LINK_LIBRARIES(resourced_shared ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+ resourced_shared ${RESOURCED_REQUIRE_PKGS_LDFLAGS} "-pie -lrt -lm -ldl"
+ )
+
+INSTALL(FILES
+ ${RESOURCED_SOURCE_DIR}/${RESOURCED}.socket
+ ${RESOURCED_SOURCE_DIR}/${RESOURCED}.service
+ DESTINATION lib/systemd/system)
+INSTALL_SYMLINK(../${RESOURCED}.socket lib/systemd/system/sockets.target.wants/${RESOURCED}.socket)
+INSTALL_SYMLINK(../${RESOURCED}.service lib/systemd/system/multi-user.target.wants/${RESOURCED}.service)
+
+IF("${NETWORK_MODULE}" STREQUAL "ON")
+ TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+ resourced
+ app-stat
+ storage
+ settings
+ net-cls
+ telephony
+ net-iface
+ )
+
+ IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
+ ADD_EXECUTABLE(test-udp-server ${UTILS_SOURCE_DIR}/udp-server.c ${UTILS_SOURCE_DIR}/udp-common.c)
+ ADD_EXECUTABLE(test-udp-client ${UTILS_SOURCE_DIR}/udp-client.c ${UTILS_SOURCE_DIR}/udp-common.c)
+ ADD_EXECUTABLE(iface-test ${UTILS_SOURCE_DIR}/iface-test.c)
+ TARGET_LINK_LIBRARIES(iface-test net-iface ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+ ENDIF()
+
+ INSTALL(FILES ${DATA_DIR}/traffic_db.sql
+ DESTINATION /usr/share)
+ INSTALL(FILES ${DATA_DIR}/exception_db.sql
+ DESTINATION /usr/share)
+ENDIF()
+
+INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/resourced.h DESTINATION include/system)
+
+SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SKIP_BUILD_RPATH true)
+INSTALL(FILES ${PROJECT_NAME}
+ DESTINATION ${MAKE_INSTALL_PREFIX}/usr/bin RENAME resourced
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE WORLD_EXECUTE)
+
+IF("${MEMORY_ENG}" STREQUAL "ON")
+ INSTALL(FILES ${MEMORY_SOURCE_DIR}/memory_eng.conf
+ DESTINATION /etc/resourced RENAME memory.conf)
+ELSE()
+ INSTALL(FILES ${MEMORY_SOURCE_DIR}/memory_user.conf
+ DESTINATION /etc/resourced RENAME memory.conf)
+ENDIF()
+
+IF("${SWAP_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${SWAP_SOURCE_DIR}/swap.conf DESTINATION /etc/resourced)
+ENDIF()
+
+IF("${HEART_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${HEART_SOURCE_DIR}/heart.conf
+ DESTINATION /etc/resourced RENAME heart.conf)
+ INSTALL(FILES ${HEART_SOURCE_DIR}/dump_heart_data.sh
+ DESTINATION /opt/etc/dump.d/module.d)
+ENDIF()
+
+IF("${CPU_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${CPU_SOURCE_DIR}/cpu.conf
+ DESTINATION /etc/resourced RENAME cpu.conf)
+ELSE()
+ INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/scripts/resourced-cpucgroup.sh DESTINATION bin)
+ENDIF()
+
+IF("${TIMER_SLACK}" STREQUAL "ON")
+ INSTALL(FILES ${TIMER_SOURCE_DIR}/timer-slack.conf
+ DESTINATION /etc/resourced RENAME timer-slack.conf)
+ENDIF()
+
+IF("${VIP_AGENT}" STREQUAL "ON")
+ ADD_EXECUTABLE(vip-release-agent ${VIP_SOURCE_DIR}/vip-release-agent.c)
+ TARGET_LINK_LIBRARIES(vip-release-agent resourced_shared dlog)
+ INSTALL(TARGETS vip-release-agent DESTINATION bin)
+ INSTALL(FILES ${VIP_SOURCE_DIR}/vip-process.conf
+ DESTINATION
+ /etc/resourced RENAME vip-process.conf)
+ENDIF()
+
+IF("${BLOCK_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${BLOCK_SOURCE_DIR}/block.conf
+ DESTINATION /etc/resourced RENAME block.conf)
+ENDIF()
+
+IF("${FREEZER_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${FREEZER_SOURCE_DIR}/freezer.conf
+ DESTINATION /etc/resourced RENAME freezer.conf)
+ INSTALL(FILES ${FREEZER_SOURCE_DIR}/libsystem-freezer.so.${ARCH} DESTINATION lib
+ RENAME libsystem-freezer.so)
+ENDIF()
+
+INSTALL(FILES ${PROC-STAT_SOURCE_DIR}/proc.conf
+ DESTINATION /etc/resourced RENAME proc.conf)
+
+ADD_EXECUTABLE(hashtest ${UTILS_SOURCE_DIR}/hashtest.c)
+TARGET_LINK_LIBRARIES(hashtest ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+
+ADD_SUBDIRECTORY(mem-stress)
+ADD_SUBDIRECTORY(${PROC-STAT_SOURCE_DIR})
+ADD_SUBDIRECTORY(${MEMPS_SOURCE_DIR})
+ADD_SUBDIRECTORY(${NETWORK_SOURCE_DIR})
+IF("${SLP_TESTS}" STREQUAL "ON")
+ ADD_SUBDIRECTORY(${TEST_DIR})
+ENDIF()
--- /dev/null
+/*
+ * 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 block-monitor.c
+ *
+ * @desc monitor file system using fanotify
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <dirent.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/fanotify.h>
+#include <sys/mount.h>
+#include <mntent.h>
+#include <Ecore.h>
+
+#include "notifier.h"
+#include "procfs.h"
+#include "proc-common.h"
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "resourced.h"
+#include "trace.h"
+#include "config-parser.h"
+#include "const.h"
+#include "file-helper.h"
+#include "block.h"
+#include "logging.h"
+
+#define FAN_MODE_ACCESS "ACCESS"
+#define FAN_MODE_READ "READ"
+#define FAN_MODE_WRITE "WRITE"
+#define FAN_MODE_DISALBE "DISALBE"
+#define MAX_LOGGING_LIMIT 0x20000
+
+enum block_mount_type {
+ BLOCK_MOUNT_ORIGINAL,
+ BLOCK_MOUNT_BIND,
+};
+
+enum block_logging_type {
+ BLOCK_LOGGING_NONE = 0x0,
+ BLOCK_LOGGING_DLOG = 0x1,
+ BLOCK_LOGGING_FILE = 0x2,
+ BLOCK_LOGGING_DB = 0x4,
+};
+
+static gboolean find_hash(gpointer key, gpointer value, gpointer user_data)
+{
+ return (gboolean)strstr((char *)user_data, (char *)key);
+}
+
+int convert_fanotify_mode(const char *mode)
+{
+ int famode = FAN_EVENT_ON_CHILD;
+ if (!strncmp(mode, FAN_MODE_ACCESS, sizeof(FAN_MODE_ACCESS)))
+ famode |= FAN_OPEN;
+ else if (!strncmp(mode, FAN_MODE_READ, sizeof(FAN_MODE_READ)))
+ famode |= FAN_CLOSE_NOWRITE;
+ else if (!strncmp(mode, FAN_MODE_WRITE, sizeof(FAN_MODE_WRITE)))
+ famode |= FAN_CLOSE_WRITE;
+ else if (!strncmp(mode, FAN_MODE_DISALBE, sizeof(FAN_MODE_DISALBE)))
+ famode = 0;
+ return famode;
+}
+
+static bool check_mount_dest(const char *path)
+{
+ bool ret = false;
+ struct mntent *mnt;
+ const char *table = "/etc/mtab";
+ FILE *fp;
+ int len = strlen(path);
+
+ fp = setmntent(table, "r");
+ if (!fp)
+ return ret;
+
+ do {
+ mnt = getmntent(fp);
+ if (mnt && !strncmp(mnt->mnt_dir, path, len)) {
+ ret = true;
+ break;
+ }
+ } while (mnt != NULL);
+ endmntent(fp);
+ return ret;
+}
+
+static void block_logging(struct block_monitor_info *bmi, pid_t pid,
+ char *label, char *filename)
+{
+ int type = bmi->logging;
+
+ if (type & BLOCK_LOGGING_DLOG)
+ _E("pid %d(%s) accessed %s", pid, label, filename);
+
+ if (type & BLOCK_LOGGING_FILE) {
+ FILE *f;
+
+ if (bmi->logpath == NULL) {
+ bmi->logpath = malloc(MAX_PATH_LENGTH);
+ if (!bmi->logpath) {
+ _E("not enough memory");
+ return;
+ }
+ snprintf(bmi->logpath, MAX_PATH_LENGTH-1, "/var/log/%s.log", strrchr(bmi->path, '/'));
+ }
+
+ f = fopen(bmi->logpath, "a+");
+ if (!f)
+ return;
+ bmi->total_loglen += fprintf(f, "pid %d(%s) accessed %s\n", pid, label, filename);
+ fclose(f);
+ if (bmi->total_loglen > MAX_LOGGING_LIMIT) {
+ if (unlink(bmi->logpath) < 0)
+ _E("fail to remove %s file\n", bmi->logpath);
+ else
+ _I("clear previous log files");
+ }
+ }
+
+ if (type & BLOCK_LOGGING_DB) {
+ struct logging_data ld;
+
+ /*
+ * label in the xattr meant owner package name.
+ * block logging needed only package name.
+ * For removing overhead, block reqeusted to write label instead of appid or pkgid.
+ */
+ ld.appid = ld.pkgid = label;
+ ld.data = filename;
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_WRITE, &ld);
+ }
+}
+
+static Eina_Bool block_monitor_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ int fd, n, ret;
+ struct fanotify_event_metadata *m;
+ union {
+ struct fanotify_event_metadata metadata;
+ char buffer[4096];
+ } data;
+ pid_t currentpid = getpid();
+ struct block_monitor_info *bmi = (struct block_monitor_info *)user_data;
+ gpointer hash = 0;
+
+ 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;
+ }
+
+ n = read(fd, &data, sizeof(data));
+ if (n < 0) {
+ _E("Failed to read fanotify event\n");
+ if (errno == EINTR || errno == EAGAIN || errno == EACCES)
+ return ECORE_CALLBACK_RENEW;
+ else
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
+ char fn[sizeof("/proc/self/fd/") + MAX_DEC_SIZE(int)];
+ char buf[MAX_PATH_LENGTH] = {0,};
+ char label[PROC_NAME_MAX];
+
+ if (m->fd < 0)
+ goto next;
+ if (m->pid == currentpid)
+ goto next;
+ snprintf(fn, sizeof(fn), "/proc/self/fd/%d", m->fd);
+ ret = readlink(fn, buf, sizeof(buf)-1);
+ if (ret < 0)
+ goto next;
+
+ if (bmi->last_skip_pid == m->pid)
+ goto next;
+
+ if (bmi->last_monitor_pid == m->pid)
+ goto logging;
+
+ hash = bmi->block_exclude_path
+ ? g_hash_table_find(bmi->block_exclude_path,
+ find_hash, (gpointer)buf)
+ : NULL;
+
+ if (hash) {
+ bmi->last_skip_pid = m->pid;
+ goto next;
+ }
+
+ ret = proc_get_label(m->pid, label);
+ if (ret < 0)
+ goto next;
+
+ if (bmi->block_include_proc) {
+ hash = g_hash_table_find(bmi->block_include_proc,
+ find_hash, (gpointer)label);
+ if (!hash) {
+ bmi->last_skip_pid = m->pid;
+ goto next;
+ }
+ }
+
+ logging:
+ bmi->last_monitor_pid = m->pid;
+ block_logging(bmi, m->pid, label, buf);
+
+ next:
+ close(m->fd);
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void block_logging_init(struct block_monitor_info *bmi)
+{
+ if (bmi->logging & BLOCK_LOGGING_DB) {
+ static const struct module_ops *heart;
+ heart = find_module("HEART");
+ if (!heart)
+ bmi->logging &= (~BLOCK_LOGGING_DB);
+ }
+}
+
+int register_fanotify(struct block_monitor_info *bmi)
+{
+ int ret;
+
+ if (!bmi || !strlen(bmi->path))
+ return RESOURCED_ERROR_NO_DATA;
+
+ _D("monitor register : path %s, mode %d", bmi->path, bmi->mode);
+
+ bmi->mfd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK | FAN_CLASS_CONTENT,
+ O_RDONLY | O_LARGEFILE | O_CLOEXEC | O_NOATIME);
+ if (bmi->mfd< 0) {
+ _E("Failed to create fanotify fd");
+ goto error;
+ }
+ if (!check_mount_dest(bmi->path)) {
+ ret = mount(bmi->path, bmi->path, 0, MS_BIND, 0);
+ if (ret) {
+ _E("Failed to mount monitor dir");
+ goto error;
+ }
+ bmi->mount = BLOCK_MOUNT_BIND;
+ }
+ if (fanotify_mark(bmi->mfd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+ bmi->mode, AT_FDCWD, bmi->path) < 0) {
+ _E("Failed to mark fsnotify for %s", bmi->path);
+ goto error;
+ }
+ bmi->fd_handler = ecore_main_fd_handler_add(
+ bmi->mfd, ECORE_FD_READ, block_monitor_cb, bmi, NULL, NULL);
+ block_logging_init(bmi);
+ return RESOURCED_ERROR_NONE;
+error:
+ if (bmi->mfd) {
+ close(bmi->mfd);
+ bmi->mfd = 0;
+ }
+ if (bmi->logpath) {
+ free(bmi->logpath);
+ bmi->logpath = NULL;
+ }
+ return RESOURCED_ERROR_FAIL;
+}
+
+void unregister_fanotify(struct block_monitor_info *bmi)
+{
+ int ret;
+
+ if (bmi) {
+ if (bmi->logpath)
+ free(bmi->logpath);
+ if (bmi->mount == BLOCK_MOUNT_BIND) {
+ ret = umount(bmi->path);
+ if (ret)
+ _E("Failed to umount partition : %s", bmi->path);
+ bmi->mount = BLOCK_MOUNT_ORIGINAL;
+ }
+ close(bmi->mfd);
+ ecore_main_fd_handler_del(bmi->fd_handler);
+ }
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 cpu.c
+ *
+ * @desc cpu module
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+#include <dirent.h>
+#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 "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "resourced.h"
+#include "trace.h"
+#include "vconf.h"
+#include "cgroup.h"
+#include "config-parser.h"
+#include "const.h"
+#include "block.h"
+#include "proc-common.h"
+#include "appinfo-list.h"
+
+#define BLOCK_CONF_FILE "/etc/resourced/block.conf"
+#define BLOCK_CONF_SECTION "MONITOR"
+
+static GSList *block_monitor_list;
+
+static void free_exclude_key(gpointer data)
+{
+ if (data)
+ free(data);
+}
+
+static int load_block_config(struct parse_result *result, void *user_data)
+{
+ struct block_monitor_info *bmi;
+
+ if (!result)
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (!result->section || !result->name)
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (!strstr(result->section, BLOCK_CONF_SECTION))
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (MATCH(result->name, "path")) {
+ bmi = calloc(1, sizeof(struct block_monitor_info));
+ if (!bmi) {
+ _E("Failed to create monitor info");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ strncpy(bmi->path, result->value, MAX_PATH_LENGTH-1);
+ block_monitor_list = g_slist_prepend(block_monitor_list, bmi);
+
+ } else if (MATCH(result->name, "mode")) {
+ bmi = (struct block_monitor_info *)g_slist_nth_data(block_monitor_list, 0);
+ SET_CONF(bmi->mode, convert_fanotify_mode(result->value));
+
+ } else if (MATCH(result->name, "include")) {
+ bmi = (struct block_monitor_info *)g_slist_nth_data(block_monitor_list, 0);
+ if (!bmi->block_include_proc)
+ bmi->block_include_proc = g_hash_table_new_full(
+ g_str_hash, g_str_equal, free_exclude_key, NULL);
+
+ } else if (MATCH(result->name, "exclude")) {
+ bmi = (struct block_monitor_info *)g_slist_nth_data(block_monitor_list, 0);
+ if (!bmi->block_exclude_path)
+ bmi->block_exclude_path = g_hash_table_new_full(
+ g_str_hash, g_str_equal, free_exclude_key, NULL);
+ g_hash_table_insert(bmi->block_exclude_path, g_strndup(result->value, strlen(result->value)),
+ GINT_TO_POINTER(1));
+
+ } else if (MATCH(result->name, "logging")) {
+ bmi = (struct block_monitor_info *)g_slist_nth_data(block_monitor_list, 0);
+ SET_CONF(bmi->logging, atoi(result->value));
+
+ } else if (MATCH(result->name, "configend")) {
+ int ret;
+
+ bmi = (struct block_monitor_info *)g_slist_nth_data(block_monitor_list, 0);
+ if (bmi->mode) {
+ ret = register_fanotify(bmi);
+ if (ret == RESOURCED_ERROR_NONE)
+ return ret;
+ }
+ block_monitor_list = g_slist_remove(block_monitor_list, bmi);
+ if (bmi->block_exclude_path)
+ g_hash_table_destroy(bmi->block_exclude_path);
+ if (bmi->block_include_proc)
+ g_hash_table_destroy(bmi->block_include_proc);
+ free(bmi);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int block_prelaunch_state(void *data)
+{
+ GSList *iter;
+ struct proc_status *ps = (struct proc_status *)data;
+ struct proc_app_info *pai = ps->pai;
+
+ if (!CHECK_BIT(pai->flags, PROC_DOWNLOADAPP))
+ return RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, block_monitor_list) {
+ struct block_monitor_info *bmi = (struct block_monitor_info *)iter->data;
+ if (!bmi->block_include_proc)
+ continue;
+
+ g_hash_table_insert(bmi->block_include_proc, g_strndup(pai->ai->pkgname, strlen(pai->ai->pkgname)),
+ GINT_TO_POINTER(1));
+ _E("insert data %s, table num : %d", pai->ai->pkgname, g_hash_table_size(bmi->block_include_proc));
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int block_booting_done(void *data)
+{
+ config_parse(BLOCK_CONF_FILE, load_block_config, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_block_init(void *data)
+{
+ register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, block_booting_done);
+ register_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, block_prelaunch_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_block_exit(void *data)
+{
+ GSList *iter, *next;
+ struct block_monitor_info *bmi;
+
+ gslist_for_each_safe(block_monitor_list, iter, next, bmi) {
+ block_monitor_list = g_slist_remove(block_monitor_list, bmi);
+ unregister_fanotify(bmi);
+ if (bmi->block_exclude_path)
+ g_hash_table_destroy(bmi->block_exclude_path);
+ if (bmi->block_include_proc)
+ g_hash_table_destroy(bmi->block_include_proc);
+ free(bmi);
+ }
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, block_booting_done);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_PRELAUNCH, block_prelaunch_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops block_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "block",
+ .init = resourced_block_init,
+ .exit = resourced_block_exit,
+};
+
+MODULE_REGISTER(&block_modules_ops)
--- /dev/null
+# mode {DISABLE, ACCESS, READ, WRITE}
+# logging { DLOG = 0x1, FILE = 0x2, DB = 0x4}
+# When adding new monitor, "configend=NULL" should be included for distributing other categories.
+[MONITOR1]
+path=/opt/usr/media
+mode=WRITE
+include=DOWNLOADABLE
+exclude=SLP_debug
+exclude_proc=mtp-responder
+exclude_proc=testmode
+logging=5
+configend=NULL
--- /dev/null
+/*
+ * 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 block.h
+ * @desc Controlling block devices and file systems
+ **/
+
+
+#ifndef __BLOCK_H__
+#define __BLOCK_H__
+
+#include <unistd.h>
+#include <glib.h>
+#include <Ecore.h>
+
+#include "resourced.h"
+#include "const.h"
+
+struct block_monitor_info {
+ int mfd;
+ int mode;
+ int logging;
+ int mount;
+ char *logpath;
+ int total_loglen;
+ pid_t last_monitor_pid;
+ pid_t last_skip_pid;
+ char path[MAX_PATH_LENGTH];
+ Ecore_Fd_Handler *fd_handler;
+ GHashTable *block_include_proc;
+ GHashTable *block_exclude_path;
+};
+
+int convert_fanotify_mode(const char *mode);
+int register_fanotify(struct block_monitor_info *bmi);
+void unregister_fanotify(struct block_monitor_info *bmi);
+
+#endif /* __BLOCK_H__ */
+
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DOT_DELIMETER '.'
+
+/*
+ * 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)
+{
+ char *dot = index(appid, DOT_DELIMETER);
+ if (!dot)
+ return false;
+
+ return index(dot, DOT_DELIMETER) != NULL;
+}
+
+void extract_pkgname(const char *appid, char *pkgname,
+ const int pkgname_size)
+{
+ char *delim_pos; /* delimeter position */
+ size_t pkgname_res_size;
+
+ if (is_base_name(appid)) {
+ strncpy(pkgname, appid, pkgname_size);
+ return;
+ }
+
+ /* no a base name case try to dedicate pkg name */
+ delim_pos = strchr(appid, DOT_DELIMETER);
+ if (delim_pos) {
+ pkgname_res_size = abs(delim_pos - appid);
+ pkgname_res_size = pkgname_res_size > pkgname_size ?
+ pkgname_size : pkgname_res_size;
+ } else
+ pkgname_res_size = pkgname_size -1;
+
+ strncpy(pkgname, appid, pkgname_res_size);
+ pkgname[pkgname_res_size] = '\0';
+}
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+
+#define PKGNAME_SIZE MAX_NAME_SIZE
+
+/**
+ * Get package name from appid.
+ * For base (rpm) packages it's the same name as appid,
+ * For SDK (tpk) packages it's 10 alpha digit before first .(dot)
+ * @param appid - given appid
+ * @param pkgname - out package name
+ * @pkgname_size - size of pkgname given buffer
+ **/
+void extract_pkgname(const char *appid, char *pkgname, const int pkgname_size);
--- /dev/null
+/*
+ * 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 appinfo-list.c
+ * @desc define helper functions to get/put appid info.
+ **/
+
+#include <resourced.h>
+#include <trace.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "macro.h"
+#include "const.h"
+#include "appinfo-list.h"
+
+static GRWLock resourced_appinfo_lock;
+static GHashTable *resourced_appinfo_list;
+
+static struct resourced_appinfo *resourced_appinfo_create(const char *appid,
+ const char *pkgname)
+{
+ struct resourced_appinfo *ai;
+ int appid_len, pkgname_len;
+
+ assert(appid != NULL);
+ assert(pkgname != NULL);
+
+ if (!appid || !pkgname){
+ _E("appid or pkgname was null, values: %s, %s", appid, pkgname);
+ return NULL;
+ }
+
+ appid_len = strlen(appid);
+ pkgname_len = strlen(pkgname);
+
+ if (appid_len >= MAX_APPID_LENGTH - 1 ||
+ pkgname_len >= MAX_PKGNAME_LENGTH - 1) {
+ _E("appid length = %d, pkgname length = %d",
+ appid_len, pkgname_len);
+ return NULL;
+ }
+
+ ai = malloc(sizeof(struct resourced_appinfo));
+ if (!ai) {
+ _E("malloc failed for resourced_appinfo");
+ return NULL;
+ }
+
+ /* appid and pkgname are terminated with null */
+ strncpy(ai->appid, appid, appid_len);
+ ai->appid[appid_len] = '\0';
+ strncpy(ai->pkgname, pkgname, pkgname_len);
+ ai->pkgname[pkgname_len] = '\0';
+ ai->ref = 0;
+
+ g_rw_lock_writer_lock(&resourced_appinfo_lock);
+
+ g_hash_table_insert(resourced_appinfo_list, (gpointer)ai->appid,
+ (gpointer)ai);
+
+ g_rw_lock_writer_unlock(&resourced_appinfo_lock);
+
+ return ai;
+}
+
+static void resourced_appinfo_remove(struct resourced_appinfo *ai)
+{
+ if (!ai)
+ return;
+
+ g_rw_lock_writer_lock(&resourced_appinfo_lock);
+
+ /* ai is freed by free notifier */
+ g_hash_table_remove(resourced_appinfo_list, (gpointer)ai->appid);
+
+ g_rw_lock_writer_unlock(&resourced_appinfo_lock);
+}
+
+struct resourced_appinfo *resourced_appinfo_get(struct resourced_appinfo *ai,
+ const char *appid, const char *pkgname)
+{
+ if (!appid)
+ return NULL;
+
+ g_rw_lock_reader_lock(&resourced_appinfo_lock);
+
+ ai = g_hash_table_lookup(resourced_appinfo_list, (gconstpointer)appid);
+
+ g_rw_lock_reader_unlock(&resourced_appinfo_lock);
+
+ if (!ai) {
+ ai = resourced_appinfo_create(appid, pkgname);
+ if (!ai)
+ return NULL;
+ }
+
+ g_atomic_int_inc(&ai->ref);
+ _D("appid %s, pkgname = %s, ref = %d", appid, pkgname,
+ g_atomic_int_get(&ai->ref));
+
+ return ai;
+}
+
+void resourced_appinfo_put(struct resourced_appinfo *ai)
+{
+ gboolean ret;
+
+ if (!ai)
+ return;
+
+ ret = g_atomic_int_dec_and_test(&ai->ref);
+
+ _D("appid %s, pkgname = %s, ref = %d", ai->appid, ai->pkgname,
+ g_atomic_int_get(&ai->ref));
+
+ if (ret)
+ resourced_appinfo_remove(ai);
+
+}
+
+static void resourced_appinfo_free_value(gpointer value)
+{
+ struct resourced_appinfo *ai = (struct resourced_appinfo *)value;
+
+ if (!ai)
+ return;
+
+ free(ai);
+}
+
+static int __attribute__ ((constructor)) resourced_appinfo_list_init(void)
+{
+ resourced_appinfo_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ resourced_appinfo_free_value);
+ if (!resourced_appinfo_list) {
+ _E("fail create resourced_appinfo_list");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ _D("resourced_appinfo_list created");
+ return RESOURCED_ERROR_NONE;
+}
+
+static int __attribute__ ((destructor)) resourced_appinfo_list_exit(void)
+{
+ assert(resourced_appinfo_list);
+
+ g_hash_table_destroy(resourced_appinfo_list);
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 appinfo-list.h
+ * @desc define helper functions to get/put appid info.
+ **/
+
+#ifndef __APPID_LIST_H__
+#define __APPID_LIST_H__
+
+#include <glib.h>
+
+struct resourced_appinfo {
+ char appid[MAX_APPID_LENGTH];
+ char pkgname[MAX_PKGNAME_LENGTH];
+ gint ref;
+};
+
+struct resourced_appinfo *resourced_appinfo_get(struct resourced_appinfo *ai,
+ const char *appid, const char *pkgname);
+void resourced_appinfo_put(struct resourced_appinfo *ai);
+#endif /*__APPID_LIST_H__*/
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Cgroup creation implementation
+ */
+
+#include "cgroup.h"
+#include "const.h"
+#include "macro.h"
+#include "util.h"
+#include "resourced.h"
+#include "trace.h"
+#include "file-helper.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h> /*mkdirat */
+#include <glib.h>
+#include <limits.h>
+#include <sys/stat.h> /*mkdirat */
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h> /*time function */
+#include <unistd.h>
+#include <sys/mount.h>
+
+#define RELEASE_AGENT "/release_agent"
+#define NOTIFY_ON_RELEASE "/notify_on_release"
+
+static bool is_cgroup_exists(const char *cgroup_full_path)
+{
+ struct stat stat_buf;
+ return stat(cgroup_full_path, &stat_buf) == 0;
+}
+
+static int create_cgroup(const char *cgroup_full_path)
+{
+ if (mkdir (cgroup_full_path,
+ S_IRUSR | S_IWUSR | S_IRGRP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+/*
+ * @desc place pid to cgroup.procs file
+ * @return 0 in case of success, errno value in case of failure
+ */
+resourced_ret_c place_pid_to_cgroup_by_fullpath(const char *cgroup_full_path,
+ const int pid)
+{
+ char buf[256];
+ int ret = cgroup_write_node(cgroup_full_path, CGROUP_FILE_NAME,
+ (u_int32_t)pid);
+
+ ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+ "Failed place all pid to cgroup %s, error %s",
+ cgroup_full_path, strerror_r(errno, buf, sizeof(buf)));
+ return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c place_pid_to_cgroup(const char *cgroup_subsystem,
+ const char *cgroup_name, const int pid)
+{
+ char buf[MAX_PATH_LENGTH];
+ snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name);
+ 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);
+ if (child_pid < 0) {
+ _E("Invalid child pid!");
+ fclose(f);
+ return RESOURCED_ERROR_FAIL;
+ }
+ resourced_ret_c ret = place_pid_to_cgroup_by_fullpath(buf, child_pid);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to put parent process %d into %s cgroup", pid, cgroup_name);
+ fclose(f);
+ return ret;
+ }
+ }
+ fclose(f);
+ return RESOURCED_ERROR_NONE;
+}
+
+int cgroup_write_node(const char *cgroup_name,
+ const char *file_name, unsigned int value)
+{
+ char buf[MAX_PATH_LENGTH];
+ snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
+ _SD("cgroup_buf %s, value %d\n", buf, value);
+ return fwrite_int(buf, value);
+}
+
+int cgroup_write_node_str(const char *cgroup_name,
+ const char *file_name, const char *string)
+{
+ char buf[MAX_PATH_LENGTH];
+ snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
+ _SD("cgroup_buf %s, string %s\n", buf, string);
+ return fwrite_str(buf, string);
+}
+
+int cgroup_read_node(const char *cgroup_name,
+ const char *file_name, unsigned int *value)
+{
+ char buf[MAX_PATH_LENGTH];
+ int ret;
+ snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
+ ret = fread_uint(buf, value);
+ _SD("cgroup_buf %s, value %d\n", buf, *value);
+ return ret;
+}
+
+int make_cgroup_subdir(const char* parentdir, const char* cgroup_name, bool *already)
+{
+ char buf[MAX_PATH_LENGTH];
+ bool cgroup_exists;
+ int ret = 0;
+
+ 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);
+
+ cgroup_exists = is_cgroup_exists(buf);
+ if (!cgroup_exists) {
+ ret = create_cgroup(buf);
+ ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+ "cpu cgroup create fail : err %d, name %s", errno,
+ cgroup_name);
+ }
+
+ if (already)
+ *already = cgroup_exists;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int mount_cgroup_subsystem(char* source, char* mount_point, char* opts)
+{
+ return mount(source, mount_point, "cgroup",
+ MS_NODEV | MS_NOSUID | MS_NOEXEC, opts);
+}
+
+int set_release_agent(const char *cgroup_subsys, const char *release_agent)
+{
+ _cleanup_free_ char *buf = NULL;
+ int r;
+
+ r = asprintf(&buf, "%s/%s", DEFAULT_CGROUP, cgroup_subsys);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = cgroup_write_node_str(buf, RELEASE_AGENT, release_agent);
+ if (r < 0)
+ return r;
+
+ return cgroup_write_node_str(buf, NOTIFY_ON_RELEASE, "1");
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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.
+ *
+ */
+
+#include <resourced.h>
+#include <sys/types.h>
+
+/*
+ * Cgroup creation interface
+ */
+
+#ifndef _CGROUP_LIBRARY_CGROUP_H_
+#define _CGROUP_LIBRARY_CGROUP_H_
+
+#define DEFAULT_CGROUP "/sys/fs/cgroup"
+#define PROC_TASK_CHILDREN "/proc/%d/task/%d/children"
+
+/**
+ * @desc Get one unsigned int value from cgroup
+ * @param cgroup_name - cgroup path
+ * @param file_name - cgroup content to write
+ * @param value - out parameter, value to fill
+ * @return negative value if error
+*/
+int cgroup_read_node(const char *cgroup_name,
+ const char *file_name, unsigned int *value);
+
+/**
+ * @desc Put value to cgroup,
+ * @param cgroup_name - cgroup path
+ * @param file_name - cgroup content to write
+ * @param value - data to write
+ * @return negative value if error
+ */
+int cgroup_write_node(const char *cgroup_name, const char *file_name, unsigned int value);
+
+/**
+ * @desc Put value to cgroup,
+ * @param cgroup_name - cgroup path
+ * @param file_name - cgroup content to write
+ * @param string -string to write
+ * @return negative value if error
+ */
+int cgroup_write_node_str(const char *cgroup_name,
+ const char *file_name, const char *string);
+
+/**
+ * @desc make cgroup,
+ * @param parentdir - parent cgroup path
+ * @param cgroup_name - cgroup subdirectory to write
+ * @param already - true if subdir already exists, NULL pointer is possible
+ * as formal argument, in this case it will not be filled
+ * @return negative value if error
+ */
+int make_cgroup_subdir(const char* parentdir, const char* cgroup_name, bool *already);
+
+/**
+ * @desc mount cgroup,
+ * @param source -cgroup name
+ * @param mount_point - cgroup path
+ * @param opts - mount options
+ * @return negative value if error
+ */
+int mount_cgroup_subsystem(char* source, char* mount_point, char* opts);
+
+/**
+ * @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,
+ const char *cgroup_name, const int pid);
+
+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);
+
+/**
+ * @desc this function sets release agent path into cgroup subsystem
+ * and enables this mechanism
+ * @param cgroup_sussys - cgroup subsystem name, it's relative path to cgroup,
+ * relativelly default cgroup path (DEFAULT_CGROUP)
+ * @param release_agent full path to release agent executable
+ * @return negative value if error
+ */
+int set_release_agent(const char *cgroup_subsys, const char *release_agent);
+
+#endif /*_CGROUP_LIBRARY_CGROUP_H_*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "util.h"
+#include "trace.h"
+#include "config-parser.h"
+
+#define MAX_SECTION 64
+
+static inline char *trim_str(char *s)
+{
+ char *t;
+ /* left trim */
+ s += strspn(s, WHITESPACE);
+
+ /* right trim */
+ for (t = strchr(s, 0); t > s; t--)
+ if (!strchr(WHITESPACE, t[-1]))
+ break;
+ *t = 0;
+ return s;
+}
+
+int config_parse(const char *file_name, int cb(struct parse_result *result,
+ void *user_data), void *user_data)
+{
+ FILE *f = NULL;
+ struct parse_result result;
+ /* use stack for parsing */
+ char line[LINE_MAX];
+ char section[MAX_SECTION];
+ char *start, *end, *name, *value;
+ int lineno = 0, ret = 0;
+
+ if (!file_name || !cb) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* open conf file */
+ f = fopen(file_name, "r");
+ if (!f) {
+ _E("Failed to open file %s", file_name);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* parsing line by line */
+ while (fgets(line, LINE_MAX, f) != NULL) {
+ lineno++;
+
+ start = line;
+ start[strcspn(start, NEWLINE)] = '\0';
+ start = trim_str(start);
+
+ if (*start == COMMENT) {
+ continue;
+ } else if (*start == '[') {
+ /* parse section */
+ end = strchr(start, ']');
+ if (!end || *end != ']') {
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ *end = '\0';
+ strncpy(section, start + 1, sizeof(section)-1);
+ section[MAX_SECTION-1] = '\0';
+ } else if (*start) {
+ /* parse name & value */
+ end = strchr(start, '=');
+ if (!end || *end != '=') {
+ ret = -EBADMSG;
+ goto error;
+ }
+ *end = '\0';
+ name = trim_str(start);
+ value = trim_str(end + 1);
+ end = strchr(value, COMMENT);
+ if (end && *end == COMMENT) {
+ *end = '\0';
+ value = trim_str(value);
+ }
+
+ result.section = section;
+ result.name = name;
+ result.value = value;
+ /* callback with parse result */
+ ret = cb(&result, user_data);
+ if (ret < 0) {
+ ret = -EBADMSG;
+ goto error;
+ }
+ }
+ }
+ _D("Success to load %s", file_name);
+ fclose(f);
+ return 0;
+
+error:
+ if (f)
+ fclose(f);
+ _E("Failed to read %s:%d!", file_name, lineno);
+ return ret;
+}
+
+static int config_table_lookup(void *table,
+ const char *section,
+ const char *lvalue,
+ ConfigParserCallback *func,
+ int *ltype,
+ void **data)
+{
+ ConfigTableItem *t;
+
+ assert(table);
+ assert(lvalue);
+ assert(func);
+ assert(ltype);
+ assert(data);
+
+ for (t = table; t->lvalue; t++) {
+
+ if (!streq(lvalue, t->lvalue))
+ continue;
+
+ if (!streq_ptr(section, t->section))
+ continue;
+
+ *func = t->cb;
+ *ltype = t->ltype;
+ *data = t->data;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Run the user supplied parser for an assignment */
+static int config_parse_table(const char *filename,
+ unsigned line,
+ void *table,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue)
+{
+ ConfigParserCallback cb = NULL;
+ int ltype = 0;
+ void *data = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = config_table_lookup(table,
+ section,
+ lvalue,
+ &cb,
+ <ype,
+ &data);
+ if (r <= 0)
+ return r;
+
+ if (cb)
+ return cb(filename,
+ line,
+ section,
+ lvalue,
+ ltype,
+ rvalue,
+ data);
+
+ return 0;
+}
+
+int config_parse_new(const char *filename, void *table)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ char *sections[MAX_SECTION] = { 0 };
+ char *section = NULL, *n, *e, l[LINE_MAX];
+ size_t len;
+ int i, r, num_section = 0;
+ bool already;
+ unsigned line = 0;
+
+ assert(filename);
+
+ f = fopen(filename, "r");
+ if (!f) {
+ _E("Failed to open file %s", filename);
+ return -errno;
+ }
+
+ while (!feof(f)) {
+ _cleanup_free_ char *lvalue = NULL, *rvalue = NULL;
+ char *rs = NULL;
+
+ if (fgets(l, LINE_MAX, f) == NULL) {
+ if (feof(f))
+ break;
+
+ _E("Failed to parse configuration file '%s': %m", filename);
+ r = -errno;
+ goto finish;
+ }
+
+ line++;
+ truncate_nl(l);
+
+ if (strchr(COMMENTS NEWLINE, *l))
+ continue;
+
+ if (*l == '[') {
+ len = strlen(l);
+ if (l[len-1] != ']') {
+ _E("Error: Invalid section header: %s", l);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ n = strndup(l+1, len-2);
+ if (!n) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ already = false;
+ for (i = 0; i < num_section; i++) {
+ if (streq(n, sections[i])) {
+ section = sections[i];
+ already = true;
+ free(n);
+ break;
+ }
+ }
+
+ if (already)
+ continue;
+
+ section = n;
+ sections[num_section] = n;
+ num_section++;
+ if (num_section > MAX_SECTION) {
+ _E("Error: max number of section reached: %d", num_section);
+ r = -EOVERFLOW;
+ goto finish;
+ }
+
+ continue;
+ }
+
+ if (!section)
+ continue;
+
+ e = strchr(l, '=');
+ if (e == NULL) {
+ _D("Warning: config: no '=' character in line '%s'.", l);
+ continue;
+ }
+
+ lvalue = strndup(l, e-l);
+ strstrip(lvalue);
+
+ rs = strstrip(e+1);
+ rvalue = strndup(rs, strlen(rs));
+ strstrip(rvalue);
+
+ r = config_parse_table(filename,
+ line,
+ table,
+ section,
+ lvalue,
+ rvalue);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ for (i=0; i<num_section; i++)
+ if (sections[i])
+ free(sections[i]);
+
+ return r;
+}
+
+int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data)
+{
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent de;
+ struct dirent *result;
+
+ d = opendir(dir);
+ if (!d) {
+ _E("Failed to open dir: %s", dir);
+ return errno;
+ }
+
+ FOREACH_DIRENT(de, d, result, return -errno) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ if (de.d_type != DT_REG)
+ continue;
+
+ r = asprintf(&path, "%s/%s", dir, de.d_name);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = fp(path, data);
+ /* Do not just break loop until parse all file of
+ * dir. Just only put log */
+ if (r < 0)
+ _D("Failed to parse config: %s", de.d_name);
+ }
+
+ return 0;
+}
+
+int config_parse_bool(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ int k;
+ bool *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ _E("Failed to parse boolean value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *b = !!k;
+
+ return 0;
+}
+
+
+int config_parse_int(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ int *i = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ *i = atoi(rvalue);
+
+ return 0;
+}
+
+int config_parse_string(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ char **s = data, *n;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ n = NULL;
+ else {
+ n = strndup(rvalue, strlen(rvalue));
+ if (!n)
+ return -ENOMEM;
+ }
+
+ free(*s);
+ *s = n;
+
+ return 0;
+}
+
+int config_parse_bytes(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ size_t *s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ *s = 0;
+ else {
+ r = parse_bytes(rvalue, s);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int config_parse_strv(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ char ***strv = data;
+ char **o = NULL, **v = NULL, **vv = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ return 0;
+
+ r = str_to_strv(rvalue, &v, WHITESPACE);
+ if (r < 0)
+ return r;
+
+ o = *strv;
+
+ r = strv_attach(o, v, &vv, true);
+ if (r < 0)
+ return r;
+
+ *strv = vv;
+
+ return 0;
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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 __CONFIG_PARSER_H__
+#define __CONFIG_PARSER_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#define MATCH(a, b) (!strncmp(a, b, strlen(a)))
+#define SET_CONF(a, b) (a = (b > 0.0 ? b : a))
+
+struct parse_result {
+ char *section;
+ char *name;
+ char *value;
+};
+
+/**
+ * @brief Parse config file and call callback\n
+ * @param[in] file_name conf file.
+ * @param[in] cb cb is called when conf file is parsed line by line.
+ * @param[in] user_data user data is passed to cb.
+ * @return 0 on success, negative if failed
+ */
+int config_parse(const char *file_name, int cb(struct parse_result *result,
+ void *user_data), void *user_data);
+
+/* Prototype for a parser for a specific configuration setting */
+typedef int (*ConfigParserCallback)(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data);
+
+typedef int (*ConfigParseFunc)(const char *path, void *data);
+
+/* Wraps information for parsing a specific configuration variable, to
+ * be stored in a simple array */
+typedef struct ConfigTableItem {
+ const char *section; /* Section */
+ const char *lvalue; /* Name of the variable */
+ ConfigParserCallback cb; /* Function that is called to
+ * parse the variable's
+ * value */
+ int ltype; /* Distinguish different
+ * variables passed to the
+ * same callback */
+ void *data; /* Where to store the
+ * variable's data */
+} ConfigTableItem;
+
+int config_parse_new(const char *filename, void *table);
+int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data);
+
+int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+int config_parse_bytes(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+#endif
+
--- /dev/null
+#ifndef _CONFIG_H_GENERATED
+#define _CONFIG_H_GENERATED
+
+#cmakedefine EXCLUDE_LIST_FULL_PATH "@EXCLUDE_LIST_FULL_PATH@"
+#cmakedefine EXCLUDE_LIST_OPT_FULL_PATH "@EXCLUDE_LIST_OPT_FULL_PATH@"
+/* It's command line arguments*/
+
+#cmakedefine DATABASE_FULL_PATH "@DATABASE_FULL_PATH@"
+
+#cmakedefine MINOR_VERSION ${MINOR_VERSION}
+#cmakedefine MAJOR_VERSION ${MAJOR_VERSION}
+#cmakedefine CONFIG_DATAUSAGE_NFACCT @CONFIG_DATAUSAGE_NFACCT@
+#cmakedefine TETHERING_FEATURE @TETHERING_FEATURE@
+
+#endif /* _CONFIG_H_GENERATED*/
--- /dev/null
+/*
+ * 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: const.h
+ *
+ */
+
+#ifndef _RESOURCED_CONST_H
+#define _RESOURCED_CONST_H
+
+#define TASK_FILE_NAME "/tasks"
+#define CGROUP_FILE_NAME "/cgroup.procs"
+#define UNKNOWN_APP "(unknown)"
+
+#define MAX_PATH_LENGTH 512
+#define MAX_NAME_LENGTH 256
+#define MAX_IFACE_LENGTH 32
+#define MAX_APPID_LENGTH 128
+#define MAX_PKGNAME_LENGTH 128
+
+#define PROC_BUF_MAX 64
+#define PROC_NAME_MAX 1024
+
+#define COMMA_DELIMETER ","
+
+#define COUNTER_UPDATE_PERIOD 60
+#define COUNTER_FLUSH_PERIOD 60
+
+#define NONE_QUOTA_ID 0
+
+#define TIME_TO_SAFE_DATA 1 /* one second */
+
+/*
+ * @desc reserved classid enums
+ * internal structure, we don't provide it externally
+*/
+enum resourced_reserved_classid {
+ RESOURCED_UNKNOWN_CLASSID,
+ RESOURCED_ALL_APP_CLASSID, /**< kernel expects 1 for
+ handling restriction for all
+ 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,
+};
+
+enum resourced_counter_state {
+ RESOURCED_DEFAULT_STATE = 0,
+ 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 */
--- /dev/null
+/*
+ * 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 edbus-handler.c
+ *
+ * @desc dbus handler using edbus interface
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <Eina.h>
+#include "trace.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "resourced.h"
+
+#define EDBUS_INIT_RETRY_COUNT 5
+
+struct edbus_list{
+ char *signal_name;
+ char *signal_path;
+
+ E_DBus_Signal_Handler *handler;
+};
+
+static struct edbus_object edbus_objects[] = {
+ { RESOURCED_DBUS_OBJECT_PATH, RESOURCED_DBUS_INTERFACE_NAME , NULL, NULL },
+ { RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP , NULL, NULL },
+ { RESOURCED_PATH_FREEZER, RESOURCED_INTERFACE_FREEZER, NULL, NULL },
+ { RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM, NULL, NULL },
+ { RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, NULL, NULL },
+ { RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING, NULL, NULL },
+ { RESOURCED_PATH_NETWORK, RESOURCED_INTERFACE_NETWORK, NULL, NULL },
+ { RESOURCED_PATH_SLUGGISH, RESOURCED_INTERFACE_SLUGGISH, NULL, NULL },
+ { RESOURCED_PATH_DBUS, RESOURCED_INTERFACE_DBUS, NULL, NULL },
+ { RESOURCED_PATH_APPOPT, RESOURCED_INTERFACE_APPOPT, NULL, NULL },
+ /* Add new object & interface here*/
+};
+
+static Eina_List *edbus_handler_list;
+static int edbus_init_val;
+static E_DBus_Connection *edbus_conn;
+static DBusPendingCall *edbus_request_name;
+
+static int append_variant(DBusMessageIter *iter,
+ const char *sig, char *param[])
+{
+ char *ch;
+ int i;
+ int int_type;
+ uint64_t int64_type;
+ DBusMessageIter arr;
+ struct dbus_byte *byte;
+
+ if (!sig || !param)
+ return 0;
+
+ 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,
+ DBUS_TYPE_INT32, &int_type);
+ break;
+ case 'u':
+ int_type = strtoul(param[i], NULL, 10);
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_UINT32, &int_type);
+ break;
+ case 't':
+ int64_type = atoll(param[i]);
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_UINT64, &int64_type);
+ break;
+ case 's':
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_STRING, ¶m[i]);
+ break;
+ case 'a':
+ ++i, ++ch;
+ switch (*ch) {
+ case 'y':
+ dbus_message_iter_open_container(iter,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &arr);
+ byte = (struct dbus_byte*)param[i];
+ dbus_message_iter_append_fixed_array(&arr,
+ DBUS_TYPE_BYTE, &(byte->data), byte->size);
+ dbus_message_iter_close_container(iter, &arr);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 'd':
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_INT32, ¶m[i]);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+void serialize_params(char *params[], size_t n, ...)
+{
+ va_list va;
+ int i = 0;
+ va_start(va, n);
+ for (i = 0; i < n; ++i) {
+ params[i] = va_arg(va, char *);
+ }
+ va_end(va);
+}
+
+DBusMessage *dbus_method_sync(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[])
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ DBusError err;
+ int r;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get error");
+ return NULL;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)", path, interface, method);
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ r = append_variant(&iter, sig, param);
+ if (r < 0) {
+ _E("append_variant error(%d)", r);
+ dbus_message_unref(msg);
+ dbus_connection_unref(conn);
+ return NULL;
+ }
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg,
+ DBUS_REPLY_TIMEOUT, &err);
+ if (dbus_error_is_set(&err)) {
+ _E("dbus_connection_send error(%s:%s)", err.name, err.message);
+ dbus_error_free(&err);
+ reply = NULL;
+ }
+
+ dbus_message_unref(msg);
+ dbus_connection_unref(conn);
+ return reply;
+}
+
+static int dbus_method_sync_pairs(const char *dest, const char *path,
+ const char *interface, const char *method,
+ int num, va_list args)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter aiter, piter;
+ DBusError err;
+ int ret, result, i;
+ char *key, *value;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get error");
+ return -EPERM;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)",
+ path, interface, method);
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{ss}", &aiter);
+
+ for (i = 0 ; i < num ; i = i + 2) {
+ key = va_arg(args, char *);
+ value = va_arg(args, char *);
+ _I("key(%s), value(%s)", key, value);
+ dbus_message_iter_open_container(&aiter, DBUS_TYPE_DICT_ENTRY, NULL, &piter);
+ dbus_message_iter_append_basic (&piter, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic (&piter, DBUS_TYPE_STRING, &value);
+ dbus_message_iter_close_container(&aiter, &piter);
+ }
+
+ dbus_message_iter_close_container(&iter, &aiter);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_REPLY_TIMEOUT, &err);
+ dbus_message_unref(msg);
+ if (!reply) {
+ _E("dbus_connection_send error(%s:%s) %s %s:%s-%s",
+ err.name, err.message, dest, path, interface, method);
+ dbus_error_free(&err);
+ return -ECOMM;
+ }
+
+ ret = dbus_message_get_args(reply, &err, DBUS_TYPE_INT32, &result, DBUS_TYPE_INVALID);
+ dbus_message_unref(reply);
+ if (!ret) {
+ _E("no message : [%s:%s] %s %s:%s-%s",
+ err.name, err.message, dest, path, interface, method);
+ dbus_error_free(&err);
+ return -ENOMSG;
+ }
+
+ return result;
+}
+
+int dbus_method_async(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[])
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ int ret;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get error");
+ return -EPERM;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("dbus_message_new_method_call(%s:%s-%s)", path, interface, method);
+ return -EBADMSG;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ ret = append_variant(&iter, sig, param);
+ if (ret < 0) {
+ _E("append_variant error(%d)", ret);
+ dbus_message_unref(msg);
+ return ret;
+ }
+
+ ret = dbus_connection_send(conn, msg, NULL);
+ dbus_message_unref(msg);
+ if (ret != TRUE) {
+ _E("dbus_connection_send error");
+ return -ECOMM;
+ }
+
+ return 0;
+}
+
+int register_edbus_interface(struct edbus_object *object)
+{
+ int ret = RESOURCED_ERROR_FAIL;
+
+ if (!object) {
+ _E("object is invalid value!");
+ return ret;
+ }
+
+ object->obj = e_dbus_object_add(edbus_conn, object->path, NULL);
+ if (!object->obj) {
+ _E("fail to add edbus obj");
+ return ret;
+ }
+
+ object->iface = e_dbus_interface_new(object->interface);
+ if (!object->iface) {
+ _E("fail to add edbus interface");
+ return ret;
+ }
+
+ e_dbus_object_interface_attach(object->obj, object->iface);
+
+ return 0;
+}
+
+E_DBus_Interface *get_edbus_interface(const char *path)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(edbus_objects); i++)
+ if (!strcmp(path, edbus_objects[i].path))
+ return edbus_objects[i].iface;
+
+ return NULL;
+}
+
+pid_t get_edbus_sender_pid(DBusMessage *msg)
+{
+ const char *sender;
+ DBusMessage *send_msg;
+ DBusPendingCall *pending;
+ DBusMessageIter iter;
+ int ret;
+ pid_t pid;
+
+ if (!msg) {
+ _E("invalid argument!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ sender = dbus_message_get_sender(msg);
+ if (!sender) {
+ _E("invalid sender!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ send_msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID");
+ if (!send_msg) {
+ _E("invalid send msg!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = dbus_message_append_args(send_msg, DBUS_TYPE_STRING,
+ &sender, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("fail to append args!");
+ dbus_message_unref(send_msg);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pending = e_dbus_message_send(edbus_conn, send_msg, NULL, -1, NULL);
+ if (!pending) {
+ _E("pending is null!");
+ dbus_message_unref(send_msg);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_unref(send_msg);
+
+ /* block until reply is received */
+ dbus_pending_call_block(pending);
+
+ msg = dbus_pending_call_steal_reply(pending);
+ dbus_pending_call_unref(pending);
+ if (!msg) {
+ _E("reply msg is null!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_iter_init(msg, &iter);
+ dbus_message_iter_get_basic(&iter, &pid);
+ dbus_message_unref(msg);
+
+ return pid;
+}
+
+static void unregister_edbus_signal_handle(void)
+{
+ Eina_List *search;
+ Eina_List *serach_next;
+ struct edbus_list *entry;
+
+ EINA_LIST_FOREACH_SAFE(edbus_handler_list, search, serach_next, entry) {
+ if (entry != NULL) {
+ e_dbus_signal_handler_del(edbus_conn, entry->handler);
+ edbus_handler_list = eina_list_remove(edbus_handler_list, entry);
+ free(entry->signal_name);
+ free(entry->signal_path);
+ free(entry);
+ }
+ }
+}
+
+int register_edbus_signal_handler(const char *path, const char *interface,
+ 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 &&
+ 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, user_data);
+
+ if (!handler) {
+ _E("fail to add edbus handler");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ entry = malloc(sizeof(struct edbus_list));
+
+ if (!entry) {
+ _E("Malloc failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ entry->signal_name = strndup(name, strlen(name)+1);
+
+ if (!entry->signal_name) {
+ _E("Malloc failed");
+ goto release_entry;
+ }
+
+ entry->signal_path = strndup(path, strlen(path)+1);
+ 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");
+ 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,
+ const char *name, const char *sig, char *param[])
+{
+ DBusMessage *msg;
+ DBusConnection *conn;
+ DBusMessageIter iter;
+ int r;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get error");
+ return -EPERM;
+ }
+
+ msg = dbus_message_new_signal(path, interface, name);
+ if (!msg) {
+ _E("fail to allocate new %s.%s signal", interface, name);
+ return -EPERM;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+ r = append_variant(&iter, sig, param);
+ if (r < 0) {
+ _E("append_variant error(%d)", r);
+ return -EPERM;
+ }
+
+ r = dbus_connection_send(conn, msg, NULL);
+ dbus_message_unref(msg);
+
+ if (r != TRUE) {
+ _E("dbus_connection_send error(%s:%s-%s)",
+ path, interface, name);
+ return -ECOMM;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int broadcast_edbus_signal(const char *path, const char *interface,
+ const char *name, int type, void *value)
+{
+ DBusConnection *conn;
+ DBusMessage *msg = dbus_message_new_signal(path, interface, name);
+ int r;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get error");
+ return -EPERM;
+ }
+
+ if (!msg) {
+ _E("fail to allocate new %s.%s signal", interface, name);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_append_args(msg, type, value, DBUS_TYPE_INVALID);
+
+ r = dbus_connection_send(conn, msg, NULL);
+ dbus_message_unref(msg);
+
+ if (r != TRUE) {
+ _E("dbus_connection_send error(%s:%s-%s)",
+ path, interface, name);
+ return -ECOMM;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c edbus_add_methods(const char *path,
+ const struct edbus_method *const edbus_methods,
+ const size_t size)
+{
+ E_DBus_Interface *iface;
+ int i;
+ int ret;
+
+ iface = get_edbus_interface(path);
+
+ if (!iface) {
+ _E("Fail to get edbus interface! Path = %s\n", path);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!edbus_methods[i].member || !edbus_methods[i].func)
+ continue;
+
+ ret = e_dbus_interface_method_add(iface,
+ edbus_methods[i].member,
+ edbus_methods[i].signature,
+ edbus_methods[i].reply_signature,
+ edbus_methods[i].func);
+ if (!ret) {
+ _E("Fail to add method %s!\n",
+ edbus_methods[i].member);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c edbus_add_signals(
+ const struct edbus_signal *const edbus_signals,
+ const size_t size)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < size; i++) {
+ ret = register_edbus_signal_handler(
+ edbus_signals[i].path,
+ edbus_signals[i].interface,
+ edbus_signals[i].name,
+ edbus_signals[i].func,
+ edbus_signals[i].user_data);
+ if (ret) {
+ _E("Fail to add signal %s, %s!\n",
+ edbus_signals[i].path, edbus_signals[i].name);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ 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;
+}
+
+int launch_system_app_by_dbus(const char *dest, const char *path,
+ const char *iface, const char *method, int num, ...)
+{
+ int ret;
+ va_list args;
+
+ va_start(args, num);
+ ret = dbus_method_sync_pairs(dest, path, iface, method, num, args);
+ va_end(args);
+ return ret;
+}
+
+static void request_name_cb(void *data, DBusMessage *msg, DBusError *error)
+{
+ DBusError err;
+ unsigned int val;
+ int r;
+
+ if (!msg) {
+ _D("invalid DBusMessage!");
+ return;
+ }
+
+ dbus_error_init(&err);
+ r = dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID);
+ if (!r) {
+ _E("no message : [%s:%s]", err.name, err.message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ _I("Request Name reply : %d", val);
+}
+
+int check_dbus_active(void)
+{
+ int ret = FALSE;
+ DBusError err;
+ DBusMessage *msg;
+ DBusMessageIter iter, sub;
+ const char *state;
+ char *pa[2];
+
+ pa[0] = "org.freedesktop.systemd1.Unit";
+ pa[1] = "ActiveState";
+
+ _I("%s %s", pa[0], pa[1]);
+
+ msg = dbus_method_sync("org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1/unit/default_2etarget",
+ "org.freedesktop.DBus.Properties",
+ "Get", "ss", pa);
+ if (!msg)
+ return -EBADMSG;
+
+ dbus_error_init(&err);
+
+ if (!dbus_message_iter_init(msg, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ goto out;
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+ ret = -EBADMSG;
+ goto out;
+ }
+
+ dbus_message_iter_get_basic(&sub, &state);
+
+ if (strncmp(state, "active", 6) == 0)
+ ret = TRUE;
+out:
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+ return ret;
+}
+
+void edbus_init(void)
+{
+ int retry = RESOURCED_ERROR_NONE;
+ int i;
+retry_init:
+ edbus_init_val = e_dbus_init();
+ if (edbus_init_val)
+ goto retry_bus_get;
+ if (retry == EDBUS_INIT_RETRY_COUNT) {
+ _E("fail to init edbus");
+ return;
+ }
+ retry++;
+ goto retry_init;
+
+retry_bus_get:
+ retry = 0;
+ edbus_conn = e_dbus_bus_get(DBUS_BUS_SYSTEM);
+ if (edbus_conn)
+ goto retry_bus_request;
+ if (retry == EDBUS_INIT_RETRY_COUNT) {
+ _E("fail to get edbus");
+ return;
+ }
+ retry++;
+ goto retry_bus_get;
+
+retry_bus_request:
+ retry = 0;
+ edbus_request_name = e_dbus_request_name(edbus_conn, RESOURCED_DBUS_BUS_NAME,
+ DBUS_NAME_FLAG_REPLACE_EXISTING, request_name_cb, NULL);
+ if (edbus_request_name)
+ goto register_objects;
+ if (retry == EDBUS_INIT_RETRY_COUNT) {
+ _E("fail to request edbus name");
+ return;
+ }
+ retry++;
+ goto retry_bus_request;
+
+register_objects:
+ for (i = 0; i < ARRAY_SIZE(edbus_objects); i++) {
+ int ret;
+
+ ret = register_edbus_interface(&edbus_objects[i]);
+ if (ret < 0) {
+ _E("fail to add obj & interface for %s",
+ edbus_objects[i].interface);
+ return;
+ }
+
+ _I("add new obj for %s", edbus_objects[i].interface);
+ }
+
+ _I("start edbus service");
+}
+
+void edbus_exit(void)
+{
+ unregister_edbus_signal_handle();
+ e_dbus_connection_close(edbus_conn);
+ e_dbus_shutdown();
+}
+
+E_DBus_Connection *get_resourced_edbus_connection(void)
+{
+ return edbus_conn;
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 edbus-handler.h
+ * @desc dbus handler using edbus interface
+ **/
+
+#ifndef __EDBUS_HANDLE_H__
+#define __EDBUS_HANDLE_H__
+
+#include <E_DBus.h>
+#include <resourced.h>
+
+struct edbus_method {
+ const char *member;
+ const char *signature;
+ const char *reply_signature;
+ E_DBus_Method_Cb func;
+};
+
+struct edbus_object {
+ const char *path;
+ const char *interface;
+ E_DBus_Object *obj;
+ E_DBus_Interface *iface;
+};
+
+struct edbus_signal {
+ const char *path;
+ const char *interface;
+ const char *name;
+ E_DBus_Signal_Cb func;
+ void *user_data;
+};
+
+#define DBUS_REPLY_TIMEOUT (120 * 1000)
+
+#define RESOURCED_DBUS_BUS_NAME "org.tizen.resourced"
+#define RESOURCED_DBUS_OBJECT_PATH "/Org/Tizen/ResourceD"
+#define RESOURCED_DBUS_INTERFACE_NAME RESOURCED_DBUS_BUS_NAME
+
+/*
+ * The EDbus method to update the resourced counters
+ * Signal is generated after the database update
+ * and store new values of the counters
+ */
+#define RESOURCED_NETWORK_UPDATE "Update"
+#define RESOURCED_NETWORK_UPDATE_FINISH "UpdateFinish"
+#define RESOURCED_NETWORK_PROCESS_RESTRICTION "ProcessRestriction"
+#define RESOURCED_NETWORK_CREATE_QUOTA "CreateQuota"
+#define RESOURCED_NETWORK_REMOVE_QUOTA "RemoveQuota"
+#define RESOURCED_NETWORK_JOIN_NET_STAT "JoinNetStat"
+#define RESOURCED_NETWORK_GET_STATS "GetStats"
+
+/*
+ * Core service
+ * get/set swap status
+ * operations about swap
+ */
+#define RESOURCED_PATH_SWAP RESOURCED_DBUS_OBJECT_PATH"/Swap"
+#define RESOURCED_INTERFACE_SWAP RESOURCED_DBUS_INTERFACE_NAME".swap"
+
+#define RESOURCED_PATH_FREEZER RESOURCED_DBUS_OBJECT_PATH"/Freezer"
+#define RESOURCED_INTERFACE_FREEZER RESOURCED_DBUS_INTERFACE_NAME".freezer"
+
+#define RESOURCED_PATH_OOM RESOURCED_DBUS_OBJECT_PATH"/Oom"
+#define RESOURCED_INTERFACE_OOM RESOURCED_DBUS_INTERFACE_NAME".oom"
+
+#define RESOURCED_PATH_NETWORK RESOURCED_DBUS_OBJECT_PATH"/Network"
+#define RESOURCED_INTERFACE_NETWORK RESOURCED_DBUS_INTERFACE_NAME".network"
+
+#define RESOURCED_PATH_PROCESS RESOURCED_DBUS_OBJECT_PATH"/Process"
+#define RESOURCED_INTERFACE_PROCESS RESOURCED_DBUS_INTERFACE_NAME".process"
+
+#define RESOURCED_PATH_DBUS RESOURCED_DBUS_OBJECT_PATH"/DBus"
+#define RESOURCED_INTERFACE_DBUS RESOURCED_DBUS_INTERFACE_NAME".dbus"
+
+#define RESOURCED_PATH_SLUGGISH RESOURCED_DBUS_OBJECT_PATH"/Sluggish"
+#define RESOURCED_INTERFACE_SLUGGISH RESOURCED_DBUS_INTERFACE_NAME".sluggish"
+
+#define RESOURCED_PATH_APPOPT RESOURCED_DBUS_OBJECT_PATH"/Appopt"
+#define RESOURCED_INTERFACE_APPOPT RESOURCED_DBUS_INTERFACE_NAME".appopt"
+
+#define SIGNAL_PROC_WATCHDOG_RESULT "WatchdogResult"
+#define SIGNAL_PROC_ACTIVE "Active"
+#define SIGNAL_PROC_EXCLUDE "ProcExclude"
+#define SIGNAL_PROC_PRELAUNCH "ProcPrelaunch"
+#define SIGNAL_PROC_SWEEP "ProcSweep"
+#define SIGNAL_PROC_WATCHDOG "ProcWatchdog"
+#define SIGNAL_PROC_SYSTEMSERVICE "SystemService"
+#define SIGNAL_PROC_EXCLUDEAPPID "ProcExcludeByAppid"
+
+#define SIGNAL_SLUGGISH_DETECTED "SluggishDetected"
+
+#define SIGNAL_OOM_SET_THRESHOLD "SetThreshold"
+#define SIGNAL_OOM_SET_LEAVE_THRESHOLD "SetLeaveThreshold"
+#define SIGNAL_OOM_TRIGGER "Trigger"
+#define SIGNAL_OOM_SET_PERCEPTIBLE "SetPerceptible"
+#define SIGNAL_OOM_SET_PLATFORM "SetPlatformSwap"
+
+#define SIGNAL_NAME_SWAP_TYPE "SwapType"
+#define SIGNAL_NAME_SWAP_START_PID "SwapStartPid"
+
+/*
+ * Logging
+ */
+#define RESOURCED_PATH_LOGGING RESOURCED_DBUS_OBJECT_PATH"/Logging"
+#define RESOURCED_INTERFACE_LOGGING RESOURCED_DBUS_INTERFACE_NAME".logging"
+
+/*
+ * System popup
+ */
+#define SYSTEM_POPUP_BUS_NAME "org.tizen.system.popup"
+#define SYSTEM_POPUP_PATH_NAME "/Org/Tizen/System/Popup"
+#define SYSTEM_POPUP_IFACE_NAME SYSTEM_POPUP_BUS_NAME
+
+#define SYSTEM_POPUP_PATH_SYSTEM SYSTEM_POPUP_PATH_NAME"/System"
+#define SYSTEM_POPUP_IFACE_SYSTEM 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
+ */
+#define DEVICED_BUS_NAME "org.tizen.system.deviced"
+#define DEVICED_OBJECT_PATH "/Org/Tizen/System/DeviceD"
+#define DEVICED_PATH_PROCESS DEVICED_OBJECT_PATH"/Process"
+#define DEVICED_INTERFACE_PROCESS DEVICED_BUS_NAME".Process"
+
+#define DEVICED_PATH_DISPLAY DEVICED_OBJECT_PATH"/Display"
+#define DEVICED_INTERFACE_DISPLAY DEVICED_BUS_NAME".display"
+
+#define DEVICED_PATH_BATTERY DEVICED_OBJECT_PATH"/Battery"
+#define DEVICED_INTERFACE_BATTERY DEVICED_BUS_NAME".Battery"
+
+#define DEVICED_PATH_CORE DEVICED_OBJECT_PATH"/Core"
+#define DEVICED_INTERFACE_CORE DEVICED_BUS_NAME".core"
+
+#define DEVICED_PATH_POWEROFF DEVICED_OBJECT_PATH"/PowerOff"
+#define DEVICED_INTERFACE_POWEROFF DEVICED_BUS_NAME".PowerOff"
+
+#define DEVICED_PATH_TIME DEVICED_OBJECT_PATH"/Time"
+#define DEVICED_INTERFACE_TIME DEVICED_BUS_NAME".Time"
+
+
+#define SIGNAL_DEVICED_LCDON "LCDOn"
+#define SIGNAL_DEVICED_LCDOFF "LCDOff"
+#define SIGNAL_DEVICED_LCDONCOMPLETE "LCDOnCompleted"
+#define SIGNAL_DEVICED_BOOTINGDONE "BootingDone"
+#define SIGNAL_DEVICED_POWEROFF_STATE "ChangeState"
+#define SIGNAL_DEVICED_SYSTEMTIME_CHANGED "SystemTimeChanged"
+#define SIGNAL_DEVICED_LOW_BATTERY "BatteryStatusLow"
+#define SIGNAL_DEVICED_EARLY_BOOTING_DONE "EarlyBootingDone"
+
+/*
+ * 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_DUMP "Dump"
+#define SIGNAL_DUMP_START "Start"
+#define SIGNAL_DUMP_FINISH "Finish"
+
+/*
+ * Crash
+ */
+#define CRASH_BUS_NAME "org.tizen.system.crash"
+#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash"
+#define CRASH_INTERFACE_NAME CRASH_BUS_NAME
+#define CRASH_PATH_CRASH CRASH_OBJECT_PATH"/Crash"
+#define CRASH_INTERFACE_CRASH CRASH_INTERFACE_NAME".Crash"
+#define PROCESS_CRASHED "ProcessCrashed"
+
+/*
+ * AMD
+ */
+#define AUL_APPSTATUS_BUS_NAME "org.tizen.aul.AppStatus"
+#define AUL_APPSTATUS_OBJECT_PATH "/Org/Tizen/Aul/AppStatus"
+#define AUL_APPSTATUS_INTERFACE_NAME AUL_APPSTATUS_BUS_NAME
+
+#define AUL_SUSPEND_BUS_NAME "org.tizen.appfw.SuspendHint"
+#define AUL_SUSPEND_OBJECT_PATH "/Org/Tizen/Appfw/SuspendHint"
+#define AUL_SUSPEND_INTERFACE_NAME AUL_SUSPEND_BUS_NAME
+
+#define SIGNAL_AMD_LAUNCH "AppLaunch"
+#define SIGNAL_AMD_RESUME "AppResume"
+#define SIGNAL_AMD_TERMINATE "AppTerminate"
+#define SIGNAL_AMD_STATE "AppStatusChange"
+#define SIGNAL_AMD_GROUP "AppGroup"
+#define SIGNAL_AMD_TERMINATED "AppTerminated"
+#define SIGNAL_AMD_SUSPNED "SuspendHint"
+
+struct dbus_byte {
+ char *data;
+ int size;
+};
+
+#define RETRY_MAX 5
+
+/*
+ * @desc helper function for filling params array
+ * That params array is used in dbus_method_sync/dbus_method_async
+ * */
+void serialize_params(char *params[], size_t n, ...);
+
+
+DBusMessage *dbus_method_sync(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[]);
+
+int dbus_method_async(const char *dest, const char *path,
+ const char *interface, const char *method,
+ const char *sig, char *param[]);
+
+int register_edbus_signal_handler(const char *path, const char *interface,
+ 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,
+ const char *name, const char *sig, char *param[]);
+int broadcast_edbus_signal(const char *path, const char *interface,
+ const char *name, int type, void *value);
+resourced_ret_c edbus_add_methods(const char *path,
+ const struct edbus_method *const edbus_methods,
+ const size_t size);
+resourced_ret_c edbus_add_signals(
+ const struct edbus_signal *const edbus_signals,
+ const size_t size);
+resourced_ret_c edbus_message_send(DBusMessage *msg);
+int register_edbus_interface(struct edbus_object *object);
+int check_dbus_active(void);
+
+int launch_system_app_by_dbus(const char *dest, const char *path,
+ const char *iface, const char *method, int num, ...);
+
+void edbus_init(void);
+void edbus_exit(void);
+
+E_DBus_Connection *get_resourced_edbus_connection(void);
+
+#endif /* __EDBUS_HANDLE_H__ */
--- /dev/null
+/*
+ * 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 file-helper.c
+ * @desc Helper functions for working with files
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "file-helper.h"
+#include "trace.h"
+#include "macro.h"
+#include "util.h"
+
+#define BUF_MAX (BUFSIZ)
+#define BUF_INC_SIZE (512 << 10)
+
+int fwrite_str(const char *path, const char *str)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret;
+
+ assert(path);
+ assert(str);
+
+ f = fopen(path, "w");
+ ret_value_errno_msg_if(!f, -errno,
+ "Fail to open file %s", path);
+
+ ret = fputs(str, f);
+ ret_value_errno_msg_if(ret == EOF, errno ? -errno : -EIO,
+ "Fail to write file");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int fwrite_int(const char *path, const int number)
+{
+ _cleanup_free_ char *digit_buf = NULL;
+ int ret;
+
+ ret = asprintf(&digit_buf, "%d", number);
+ ret_value_errno_msg_if(ret < 0, -ENOMEM,
+ "sprintf failed\n");
+
+ return fwrite_str(path, digit_buf);
+}
+
+int fwrite_uint(const char *path, const u_int32_t number)
+{
+ _cleanup_free_ char *digit_buf = NULL;
+ int ret;
+
+ ret = asprintf(&digit_buf, "%d", number);
+ ret_value_errno_msg_if(ret < 0, -ENOMEM,
+ "sprintf failed\n");
+
+ return fwrite_str(path, digit_buf);
+}
+
+int fread_int(const char *path, int32_t *number)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret;
+
+ f = fopen(path, "r");
+ ret_value_errno_msg_if(!f, -errno,
+ "Fail to open %s file.", path);
+
+ ret = fscanf(f, "%d", number);
+ ret_value_errno_msg_if(ret == EOF, -errno,
+ "Fail to read file\n");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int fread_uint(const char *path, u_int32_t *number)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret;
+
+ f = fopen(path, "r");
+ ret_value_errno_msg_if(!f, -errno,
+ "Fail to open %s file.", path);
+
+ ret = fscanf(f, "%u", number);
+ ret_value_errno_msg_if(ret == EOF, -errno,
+ "Fail to read file\n");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int fwrite_array(const char *path, const void *array,
+ const size_t size_of_elem,
+ const size_t numb_of_elem)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret;
+
+ assert(path);
+ assert(array);
+
+ f = fopen(path, "w");
+ ret_value_errno_msg_if(!f, -errno,
+ "Failed open %s file", path);
+
+ ret = fwrite(array, size_of_elem, numb_of_elem, f);
+ ret_value_errno_msg_if(ret != numb_of_elem, -errno,
+ "Failed write array into %s file", path);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/* reads file contents into memory */
+char* cread(const char* path)
+{
+ char* text = NULL;
+ size_t size = 0;
+
+ ssize_t ret;
+ char* ptr = text;
+ size_t cap = size;
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ _E("Failed to open %s: %m", path);
+ return NULL;
+ }
+
+ do {
+ /* ensure we have enough space */
+ if (cap == 0) {
+ ptr = (char*)realloc(text, size + BUF_INC_SIZE);
+ if (ptr == NULL) {
+ ret = -1;
+ break;
+ }
+
+ text = ptr;
+ ptr = text + size;
+ cap = BUF_INC_SIZE;
+ size += BUF_INC_SIZE;
+ }
+ ret = read(fd, ptr, cap);
+ if (ret == 0) {
+ *ptr = 0;
+ } else if (ret > 0) {
+ cap -= ret;
+ ptr += ret;
+ } else
+ free(text);
+ } while (ret > 0);
+
+ return (ret < 0 ? NULL : text);
+}
+
+/* like fgets/gets but adjusting contents pointer */
+char* cgets(char** contents)
+{
+ if (contents && *contents && **contents) {
+ char* bos = *contents; /* begin of string */
+ char* eos = strchr(bos, '\n'); /* end of string */
+
+ if (eos) {
+ *contents = eos + 1;
+ *eos = 0;
+ } else {
+ *contents = NULL;
+ }
+
+ return bos;
+ }
+
+ return NULL;
+}
+
+int copy_file(char *dest, char *src)
+{
+ _cleanup_fclose_ FILE *fps = NULL;
+ _cleanup_fclose_ FILE *fpd = NULL;
+ char buf[BUF_MAX];
+ size_t size;
+
+ fps = fopen(src, "rb");
+ if (fps == NULL) {
+ _E("Failed to open src file '%s': %m", src);
+ return -errno;
+ }
+
+ fpd = fopen(dest, "wb");
+ if (fpd == NULL) {
+ _E("Failed to open dest file '%s': %m", dest);
+ return -errno;
+ }
+
+ while ((size = fread(buf, 1, BUF_MAX, fps))) {
+ fwrite(buf, 1, size, fpd);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 file-helper.h
+ * @desc Helper functions for working with files
+ */
+
+#ifndef _RESOURCED_FILE_HELPER_H_
+#define _RESOURCED_FILE_HELPER_H_
+
+#include "resourced.h"
+
+/**
+ * @desc write string to the file
+ * @param path - path to the file, str - string is written to the file
+ * @return negative value if error
+ */
+int fwrite_str(const char *path, const char *str);
+
+int fwrite_int(const char *path, const int number);
+
+int fwrite_uint(const char *path, const u_int32_t number);
+
+int fread_int(const char *path, int32_t *number);
+
+int fread_uint(const char *path, u_int32_t *number);
+
+int fwrite_array(const char *path, const void *array,
+ const size_t size_of_elem,
+ const size_t numb_of_elem);
+
+char *cread(const char *path);
+char *cgets(char **contents);
+
+/**
+ * @desc copy file from src to dest
+ * @param dest- destination file path, src- source file path
+ * @return negative value if error
+ */
+int copy_file(char *dest, char *src);
+
+#endif /*_RESOURCED_FILE_HELPER_H_*/
--- /dev/null
+/*
+ * 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 filemap-helper.c
+ *
+ * @desc filemap for storing (key, value) based on tree
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <glib.h>
+
+#include "const.h"
+#include "heart.h"
+#include "resourced.h"
+#include "trace.h"
+#include "macro.h"
+#include "filemap.h"
+
+static int filemap_exist(const char *fname)
+{
+ FILE *fp;
+
+ fp = fopen(fname, "r");
+
+ if (!fp)
+ return RESOURCED_ERROR_FAIL;
+
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+int filemap_new(struct filemap **fm, const char *fname,
+ int size, int check_exist)
+{
+ int exist = -1;
+ int fd;
+
+ assert(fm);
+
+ /*
+ * when filemap already exists and caller wants to
+ * keep contents previously stored in the filemap by
+ * calling with check_exist set, check if there is
+ * filemap with path before it tries to open it.
+ */
+ if (check_exist)
+ exist = filemap_exist(fname);
+
+ fd = open(fname, O_RDWR | O_CREAT, 0400);
+
+ if (fd < 0) {
+ _E("%s open error %d", fname, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ _E("%s ftruncate failed", fname);
+ close(fd);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ *fm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, 0);
+
+ if (*fm == MAP_FAILED) {
+ _E("%s mmap failed", fname);
+ close(fd);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ (*fm)->filemap_size = size;
+ (*fm)->filemap_data_size = size - sizeof(struct filemap);
+ /* root start right after filemap */
+ (*fm)->root = sizeof(struct filemap);
+
+ /*
+ * allocate node for root and set byte_used after root.
+ * when filemap exists already and check_exit is set,
+ * we use byte_used value stored in existing filemap.
+ */
+ if (exist < 0) {
+ _D("filemap does not exist");
+ (*fm)->byte_used = sizeof(struct filemap_node);
+ }
+
+ close(fd);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+void filemap_destroy(struct filemap *fm)
+{
+ assert(fm);
+ munmap(fm, fm->filemap_size);
+}
+
+static void *filemap_obj_alloc(struct filemap *fm, size_t size, unsigned *off)
+{
+
+ if (fm->byte_used + size > fm->filemap_data_size) {
+ _E("filemap_data_size is exceeded");
+ return NULL;
+ }
+
+ *off = fm->byte_used;
+ fm->byte_used += size;
+
+ return fm + fm->root + *off;
+}
+
+static struct filemap_node *filemap_node_init(void *start, const char *key,
+ unsigned keylen)
+{
+ struct filemap_node *fn = start;
+
+ assert(keylen < FILEMAP_MAX_KEY_LEN - 1);
+ assert(start);
+
+ fn->keylen = keylen;
+ strncpy(fn->key, key, keylen + 1);
+
+ return fn;
+}
+
+static struct filemap_info *filemap_info_init(void *start, const char *key,
+ unsigned keylen, const char *value,
+ unsigned valuelen)
+{
+ struct filemap_info *fi = start;
+
+ assert(start);
+ assert(keylen < FILEMAP_MAX_KEY_LEN - 1);
+ assert(valuelen < FILEMAP_MAX_VALUE_LEN - 1);
+
+ fi->keylen = keylen;
+ strncpy(fi->key, key, keylen + 1);
+ strncpy(fi->value, value, valuelen + 1);
+
+ return fi;
+}
+
+static struct filemap_node *filemap_node_new(struct filemap *fm, const char *key,
+ unsigned keylen, unsigned *off)
+{
+ struct filemap_node *fn;
+ unsigned offset;
+ void *p;
+
+ p = filemap_obj_alloc(fm, sizeof(struct filemap_node), &offset);
+
+ if (!p) {
+ _E("fail to allocate filemap_node");
+ return NULL;
+ }
+
+ fn = filemap_node_init(p, key, keylen);
+ *off = offset;
+
+ return fn;
+}
+
+static struct filemap_info *filemap_info_new(struct filemap *fm, const char *key,
+ unsigned keylen, const char *value,
+ unsigned valuelen, unsigned *off)
+{
+ struct filemap_info *fi;
+ unsigned offset;
+ void *p;
+
+ p = filemap_obj_alloc(fm, sizeof(struct filemap_info), &offset);
+
+ if (!p) {
+ _E("fail to allocate filemap_info");
+ return NULL;
+ }
+
+ fi = filemap_info_init(p, key, keylen, value, valuelen);
+ *off = offset;
+
+ return fi;
+}
+
+static void *filemap_to_obj(struct filemap *fm, unsigned off)
+{
+ if (!fm || off > fm->filemap_data_size)
+ return NULL;
+
+ return fm + fm->root + off;
+}
+
+static struct filemap_node *filemap_to_node(struct filemap *fm, unsigned *p_off)
+{
+ unsigned off = g_atomic_int_get(p_off);
+
+ return (struct filemap_node *)filemap_to_obj(fm, off);
+}
+
+static struct filemap_info *filemap_to_info(struct filemap *fm, unsigned *p_off)
+{
+ unsigned off = g_atomic_int_get(p_off);
+
+ return (struct filemap_info *)filemap_to_obj(fm, off);
+}
+
+struct filemap_node *filemap_root_node(struct filemap *fm)
+{
+ return (struct filemap_node *)filemap_to_obj(fm, 0);
+}
+
+static int filemap_key_cmp(const char *na, const unsigned na_len,
+ const char *nb, const unsigned nb_len)
+{
+
+ if (na_len < nb_len)
+ return -1;
+ else if (na_len > nb_len)
+ return 1;
+ else
+ return strncmp(na, nb, na_len);
+}
+
+static struct filemap_node *filemap_node_find(struct filemap *fm, struct filemap_node *fn,
+ const char *key, unsigned keylen)
+{
+ struct filemap_node *current = fn;
+ int ret;
+
+ while (true) {
+ if (!current)
+ return NULL;
+
+ ret = filemap_key_cmp(key, keylen, current->key,
+ current->keylen);
+ if (ret == 0)
+ return current;
+
+ if (ret < 0) {
+ unsigned left = g_atomic_int_get(¤t->left);
+
+ if (left != 0) {
+ current = filemap_to_node(fm, ¤t->left);
+ } else {
+ unsigned new_offset;
+
+ struct filemap_node *fn = filemap_node_new(fm, key,
+ keylen, &new_offset);
+
+ _D("insert left %s", key);
+ if (fn)
+ g_atomic_int_set(¤t->left,
+ new_offset);
+ return fn;
+ }
+ } else {
+ unsigned right = g_atomic_int_get(¤t->right);
+
+ if (right != 0) {
+ current = filemap_to_node(fm, ¤t->right);
+ } else {
+ unsigned new_offset;
+ struct filemap_node *fn = filemap_node_new(fm, key,
+ keylen, &new_offset);
+
+ _D("insert right %s", key);
+ if (fn)
+ g_atomic_int_set(¤t->right,
+ new_offset);
+ return fn;
+ }
+ }
+ }
+}
+
+static struct filemap_info *filemap_entry_find(struct filemap *fm, struct filemap_node *fn,
+ const char *key, unsigned keylen,
+ const char *value, unsigned valuelen,
+ unsigned *offset)
+{
+ struct filemap_node *current;
+ struct filemap_info *fi;
+ const char *remaining = key;
+
+ if (!fn)
+ return NULL;
+
+ current = fn;
+
+ while (true) {
+ unsigned str_size;
+ struct filemap_node *root;
+ int completed = 0;
+ unsigned children;
+ char *sep;
+
+ _D("remaining %s", remaining);
+
+ sep = strchr(remaining, '.');
+
+ if (!sep) {
+ completed = 1;
+ str_size = strlen(remaining);
+ } else {
+ str_size = sep - remaining;
+ }
+
+ if (!str_size)
+ return NULL;
+
+ children = g_atomic_int_get(¤t->children);
+
+ if (children) {
+ root = filemap_to_node(fm, ¤t->children);
+ } else {
+ unsigned new_offset;
+ root = filemap_node_new(fm, remaining, str_size, &new_offset);
+ _D("insert node child remaining = %s",remaining);
+ if (root)
+ g_atomic_int_set(¤t->children, new_offset);
+ }
+
+ if (!root)
+ return NULL;
+
+ current = filemap_node_find(fm, root, remaining, str_size);
+ if (!current) {
+ _D("cannot find bt");
+ return NULL;
+ }
+ _D("current = %s", current->key);
+
+ if (completed)
+ break;
+
+ remaining = sep + 1;
+ }
+
+ *offset = g_atomic_int_get(¤t->info);
+ if (*offset) {
+ return filemap_to_info(fm, ¤t->info);
+ }
+
+ fi = filemap_info_new(fm, key, keylen, value, valuelen, offset);
+
+ if (fi)
+ g_atomic_int_set(¤t->info, *offset);
+
+ return fi;
+}
+
+static void filemap_entry_update(struct filemap_info *fi,
+ const char *value, unsigned len)
+{
+ if (len >= FILEMAP_MAX_VALUE_LEN)
+ len = FILEMAP_MAX_VALUE_LEN;
+
+ assert(fi);
+ memcpy(fi->value, value, len);
+}
+
+int filemap_write(struct filemap *fm, const char *key, const char *value,
+ unsigned *offset)
+{
+ struct filemap_node *root = filemap_root_node(fm);
+ struct filemap_info *fi = NULL;
+ unsigned keylen = strlen(key);
+ unsigned valuelen = strlen(value);
+
+ if (*offset) {
+ fi = filemap_to_info(fm, offset);
+ if (fi && !strcmp(fi->key, key))
+ _D("fi for key %s is found using offset", fi->key);
+ else
+ fi = NULL;
+ }
+
+ if (!fi)
+ fi = filemap_entry_find(fm, root, key, keylen,
+ value, valuelen, offset);
+
+ if (!fi) {
+ _E("cannot find and add entry for %s", key);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ filemap_entry_update(fi, value, valuelen);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int filemap_foreach_read(struct filemap *fm, struct filemap_node *fn,
+ void (*callbackfn)(const struct filemap_info *fi))
+{
+ unsigned offset;
+ int ret;
+
+ if (!fn)
+ return RESOURCED_ERROR_FAIL;
+
+ offset = g_atomic_int_get(&fn->left);
+ if (offset != 0) {
+ ret = filemap_foreach_read(fm, filemap_to_node(fm, &fn->left), callbackfn);
+ if (ret < 0)
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ offset = g_atomic_int_get(&fn->info);
+ if (offset != 0) {
+ struct filemap_info *fi = filemap_to_info(fm, &fn->info);
+ if (!fi)
+ return RESOURCED_ERROR_FAIL;
+ callbackfn(fi);
+ }
+
+ offset = g_atomic_int_get(&fn->children);
+ if (offset != 0) {
+ ret = filemap_foreach_read(fm, filemap_to_node(fm, &fn->children),
+ callbackfn);
+ if (ret < 0)
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ offset = g_atomic_int_get(&fn->right);
+ if (offset != 0) {
+ ret = filemap_foreach_read(fm, filemap_to_node(fm, &fn->right), callbackfn);
+ if (ret < 0)
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 filemap-helper.h
+ * @desc filemap structure and functions
+ **/
+
+#ifndef __FILEMAP_HELPER_H_
+#define __FILEMAP_HELPER_H__
+
+#define FILEMAP_DEFAULT_SIZE
+#define FILEMAP_MAX_KEY_LEN 1024
+#define FILEMAP_MAX_VALUE_LEN 1024
+
+struct filemap {
+ unsigned byte_used;
+ unsigned filemap_size;
+ unsigned filemap_data_size;
+ unsigned root; //start of the root in the map
+};
+
+struct filemap_info {
+ unsigned keylen;
+ char value[FILEMAP_MAX_VALUE_LEN];
+ char key[FILEMAP_MAX_KEY_LEN];
+};
+
+struct filemap_node {
+ unsigned keylen;
+ unsigned info;
+ unsigned left;
+ unsigned right;
+ unsigned children;
+ char key[FILEMAP_MAX_KEY_LEN];
+};
+
+int filemap_new(struct filemap **fm, const char *fname, int size, int check_exist);
+void filemap_destroy(struct filemap *fm);
+int filemap_write(struct filemap *fm, const char *key, const char *value,
+ unsigned *offset);
+int filemap_foreach_read(struct filemap *fm, struct filemap_node *fn,
+ void (*callbackfn)(const struct filemap_info *fi));
+struct filemap_node *filemap_root_node(struct filemap *fm);
+
+#endif /*__FILEMAP_HELPER_H__*/
--- /dev/null
+/*
+ * genl.h
+ *
+ * Samsung Traffic Counter Module
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * @brief Trace macro definitions.
+ *
+ */
+
+#ifndef _KERNEL_MODULE_TRAFFIC_STAT_GEN_NETLINK_H_
+#define _KERNEL_MODULE_TRAFFIC_STAT_GEN_NETLINK_H_
+
+/* attributes*/
+enum {
+ TRAF_STAT_A_UNSPEC,
+ TRAF_STAT_A_MSG,
+ TRAF_STAT_DATA_IN,
+ TRAF_STAT_DATA_OUT,
+ TRAF_STAT_COUNT,
+ TRAF_STAT_DATA_RESTRICTION,
+ __TRAF_STAT_A_MAX,
+};
+
+/*
+ * commands: enumeration of all commands (functions),
+ * used by userspace application to identify command to be executed
+ */
+enum {
+ TRAF_STAT_C_UNSPEC,
+ TRAF_STAT_C_START,
+ TRAF_STAT_C_GET_PID_OUT,
+ TRAF_STAT_C_GET_CONN_IN,
+ TRAF_STAT_C_STOP,
+ TRAF_STAT_C_SET_RESTRICTIONS,
+ __TRAF_STAT_C_MAX,
+};
+
+enum {
+ RESTRICTION_NOTI_A_UNSPEC,
+ RESTRICTION_A_CLASSID,
+ RESTRICTION_A_IFINDEX,
+ __RESTRICTION_NOTI_A_MAX,
+};
+
+enum {
+ RESTRICTION_NOTI_C_UNSPEC,
+ RESTRICTION_NOTI_C_ACTIVE,
+ RESTRICTION_NOTI_C_WARNING,
+ __RESTRICTION_NOTI_C_MAX,
+};
+
+enum {
+ NET_ACTIVITY_A_UNSPEC,
+ NET_ACTIVITY_A_DATA_IN,
+ NET_ACTIVITY_A_DATA_OUT,
+ __NET_ACTIVITY_A_MAX,
+};
+
+enum {
+ NET_ACTIVITY_C_UNSPEC,
+ NET_ACTIVITY_C_START,
+ NET_ACTIVITY_C_STOP,
+ __NET_ACTIVITY_C_MAX,
+};
+
+#endif /*_KERNEL_MODULE_TRAFFIC_STAT_GEN_NETLINK_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 heart-common.h
+ * @desc heart common interface
+ **/
+
+#ifndef __HEART_COMMON_H__
+#define __HEART_COMMON_H__
+
+#include <stdio.h>
+#include <time.h>
+#include "const.h"
+
+/* period data types */
+enum heart_data_period {
+ DATA_LATEST,
+ DATA_3HOUR,
+ DATA_6HOUR,
+ DATA_12HOUR,
+ DATA_1DAY,
+ DATA_1WEEK,
+};
+
+struct heart_cpu_data {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ unsigned long utime;
+ unsigned long stime;
+};
+
+struct heart_app_usage {
+ char *appid;
+ char *pkgid;
+ int fg_count;
+ time_t used_time;
+ int point;
+};
+
+struct heart_memory_data {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ unsigned int max_pss;
+ unsigned int avg_pss;
+ unsigned int max_uss;
+ unsigned int avg_uss;
+};
+
+struct heart_battery_capacity {
+ time_t timestamp;
+ int capacity;
+ int diff_capacity;
+ long used_time;
+ long charging_time;
+ int charger_status;
+ int reset_mark;
+};
+/* battery capacity history*/
+int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int max_size);
+int heart_battery_get_capacity_history(GArray *arrays, enum heart_data_period period);
+
+/* cpu */
+int heart_cpu_get_table(GArray *arrays, enum heart_data_period period);
+struct heart_cpu_data *heart_cpu_get_data(char *appid, enum heart_data_period period);
+int heart_cpu_get_appusage_list(GHashTable *lists, int top);
+
+/* memory */
+int heart_memory_get_query(GArray *arrays, enum heart_data_period period);
+int heart_memory_get_foreach(GArray *arrays, enum heart_data_period period);
+int heart_memory_get_table(GArray *arrays, enum heart_data_period period);
+int heart_memory_save(void);
+struct heart_memory_data *heart_memory_get_data(char *appid, enum heart_data_period period);
+int heart_memory_get_latest_data(char *appid, unsigned int *pss, unsigned int *uss);
+
+#endif /* __heart_COMMON_H__ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 lowmem-common.h
+ * @desc lowmem common process
+ **/
+
+#ifndef __LOWMEM_COMMON_H__
+#define __LOWMEM_COMMON_H__
+
+enum lowmem_control_type {
+ LOWMEM_MOVE_CGROUP,
+ LOWMEM_MANAGE_FOREGROUND,
+};
+
+struct lowmem_data_type {
+ enum lowmem_control_type control_type;
+ int args[2];
+};
+
+#endif /* __LOWMEM_COMMON_H__ */
--- /dev/null
+/*
+ * 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: macro.h
+ *
+ * @desc general macros
+ */
+
+
+#ifndef _RESOURCED_MACRO_H_
+#define _RESOURCED_MACRO_H_
+
+#define execute_once \
+ static int __func__##guardian; \
+ for (; \
+ __func__##guardian == 0; \
+ __func__##guardian = 1)
+
+#include <assert.h>
+#include <stdio.h>
+#include <config.h>
+
+#ifndef API
+#define API __attribute__((visibility("default")))
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+#define DECLARE_WRAPPER(fn_name, inner_fn) \
+ static void fn_name(void *data, void __attribute__((__unused__)) *not_used) \
+ { \
+ return inner_fn(data); \
+ }
+
+#define UNUSED __attribute__((__unused__))
+
+#define MAX_SIZE2(a, b) sizeof(a) + sizeof(b) - 1
+#define MAX_SIZE3(a, b, c) MAX_SIZE2(a, b) + sizeof(c) - 1
+
+/*
+ * One byte digit has 3 position in decimal representation
+ * 2 - 5
+ * 4 - 10
+ * 8 - 20
+ * >8 - compile time error
+ * plus 1 null termination byte
+ * plus 1 for negative prefix
+ */
+#define MAX_DEC_SIZE(type) \
+ (2 + (sizeof(type) <= 1 ? 3 : \
+ sizeof(type) <= 2 ? 5 : \
+ sizeof(type) <= 4 ? 10 : \
+ 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); \
+ return; \
+ } \
+} while (0)
+
+#define ret_value_if(expr, val) do { \
+ if (expr) { \
+ _E("(%s) -> %s():%d return", #expr, __FUNCTION__, __LINE__); \
+ return (val); \
+ } \
+} while (0)
+
+#define ret_value_msg_if(expr, val, fmt, arg...) do { \
+ if (expr) { \
+ _E(fmt, ##arg); \
+ return val; \
+ } \
+} while (0)
+
+#define ret_value_secure_msg_if(expr, val, fmt, arg...) do { \
+ if (expr) { \
+ _SE(fmt, ##arg); \
+ return val; \
+ } \
+} while (0)
+
+#define ret_value_errno_msg_if(expr, val, fmt, arg...) do { \
+ if (expr) { \
+ ETRACE_ERRNO_MSG(fmt, ##arg); \
+ return val; \
+ } \
+} while (0)
+
+/*
+ * @brief Copy from source to destination
+ * destination should not be on heap.
+ * Destination will be null terminated
+ */
+#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)
+
+/* FIXME: Do we really need pointers? */
+#define array_foreach(key, type, array) \
+ guint _array_foreach_index; \
+ type *key; \
+ for (_array_foreach_index = 0; \
+ array && _array_foreach_index < array->len && \
+ (key = &g_array_index(array, type, _array_foreach_index)); \
+ ++_array_foreach_index)
+
+#define slist_foreach(key, type, list) \
+ type *key; \
+ GSList *_slist_foreach_copy_list = list; \
+ for (; \
+ _slist_foreach_copy_list && \
+ ((key = _slist_foreach_copy_list->data) || 1); \
+ _slist_foreach_copy_list = _slist_foreach_copy_list->next)
+
+#define gslist_for_each_item(item, list) \
+ for(item = list; item != NULL; item = g_slist_next(item))
+
+#define gslist_for_each(head, elem, node) \
+ 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_slist_next(elem), node = NULL; \
+ elem && ((node = elem->data) != NULL); \
+ elem = elem_next, elem_next = g_slist_next(elem), node = NULL)
+
+#define DB_ACTION(command) do { \
+ if ((command) != SQLITE_OK) { \
+ error_code = RESOURCED_ERROR_DB_FAILED; \
+ goto handle_error; \
+ } \
+} while (0)
+
+#define MODULE_REGISTER(module) \
+ static void __attribute__ ((constructor)) module_init(void) \
+ { \
+ add_module(module); \
+ } \
+ static void __attribute__ ((destructor)) module_exit(void) \
+ { \
+ remove_module(module); \
+ }
+
+#endif /* _RESOURCED_MACRO_H_ */
--- /dev/null
+%{
+#include "procfs.h"
+%}
+meminfo_mapping;
+%language=ANSI-C
+%define slot-name name
+%define hash-function-name meminfo_mapping_hash
+%define lookup-function-name meminfo_mapping_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+MemTotal, MEMINFO_ID_MEM_TOTAL
+MemFree, MEMINFO_ID_MEM_FREE
+MemAvailable, MEMINFO_ID_MEM_AVAILABLE
+Buffers, MEMINFO_ID_BUFFERS
+Cached, MEMINFO_ID_CACHED
+SwapCached, MEMINFO_ID_SWAP_CACHED
+Active, MEMINFO_ID_ACTIVE
+Inactive, MEMINFO_ID_INACTIVE
+Active(anon), MEMINFO_ID_ACTIVE_ANON
+Inactive(anon), MEMINFO_ID_INACTIVE_ANON
+Active(file), MEMINFO_ID_ACTIVE_FILE
+Inactive(file), MEMINFO_ID_INACTIVE_FILE
+Unevictable, MEMINFO_ID_UNEVICTABLE
+Mlocked, MEMINFO_ID_MLOCKED
+HighTotal, MEMINFO_ID_HIGH_TOTAL
+HighFree, MEMINFO_ID_HIGH_FREE
+LowTotal, MEMINFO_ID_LOW_TOTAL
+LowFree, MEMINFO_ID_LOW_FREE
+SwapTotal, MEMINFO_ID_SWAP_TOTAL
+SwapFree, MEMINFO_ID_SWAP_FREE
+Dirty, MEMINFO_ID_DIRTY
+Writeback, MEMINFO_ID_WRITEBACK
+AnonPages, MEMINFO_ID_ANON_PAGES
+Mapped, MEMINFO_ID_MAPPED
+Shmem, MEMINFO_ID_SHMEM
+Slab, MEMINFO_ID_SLAB
+SReclaimable, MEMINFO_ID_SRECLAIMABLE
+SUnreclaim, MEMINFO_ID_SUNRECLAIM
+KernelStack, MEMINFO_ID_KERNEL_STACK
+PageTables, MEMINFO_ID_PAGE_TABLES
+NFS_Unstable, MEMINFO_ID_NFS_UNSTABLE
+Bounce, MEMINFO_ID_BOUNCE
+WritebackTmp, MEMINFO_ID_WRITEBACK_TMP
+CommitLimit, MEMINFO_ID_COMMIT_LIMIT
+Committed_AS, MEMINFO_ID_COMMITTED_AS
+VmallocTotal, MEMINFO_ID_VMALLOC_TOTAL
+VmallocUsed, MEMINFO_ID_VMALLOC_USED
+VmallocChunk, MEMINFO_ID_VMALLOC_CHUNK
--- /dev/null
+/*
+ * 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 memory-common.h
+ * @desc header file for handling memory cgroups
+ **/
+
+#ifndef __MEMORY_COMMONL_H__
+#define __MEMORY_COMMONL_H__
+
+#include <glib.h>
+#include "const.h"
+
+/*
+ * [memory cgroup information]
+ * MEMCG_MEMORY : root cgroup for system daemons
+ * MEMCG_PLATFORM : platform cgroup for swapping desired platform daemons
+ * MEMCG_FOREGROUND : foreground cgroup (oom : 150 ~ 200)
+ * MEMCG_PREVIOUS : previous cgroup including service and perciptible (oom : 230 ~ 300)
+ * MEMCG_FAVORITE : recently used application (oom : 270)
+ * MEMCG_BACKGROUND : background cgroup (oom : 330 ~ )
+ * MEMCG_SWAP : swap cgroup (select victims from background applications)
+ */
+enum memcg_type {
+ MEMCG_MEMORY,
+ MEMCG_PLATFORM,
+ MEMCG_FOREGROUND,
+ MEMCG_PREVIOUS,
+ MEMCG_FAVORITE,
+ MEMCG_BACKGROUND,
+ MEMCG_SWAP,
+ MEMCG_MAX,
+};
+
+enum {
+ LOWMEM_NORMAL,
+ LOWMEM_SWAP,
+ LOWMEM_LOW,
+ LOWMEM_MEDIUM,
+ LOWMEM_MAX_LEVEL,
+};
+
+/* 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 {
+ /* 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 {
+ /* number of sub cgroups */
+ int num_subcgroup;
+ /* parent cgroup */
+ struct memcg_info *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 *memcg_info, float ratio,
+ unsigned int totalram);
+void memcg_info_init(struct memcg_info *memcg_info, const char *name);
+void memcg_init(struct memcg *memcg);
+void memcg_show(struct memcg *memcg);
+int memcg_add_cgroups(struct memcg *memcg, int num);
+
+/**
+ * @desc get anon memory usage of cgroup mi based on memory.stat
+ * @return 0 if the value was correctly read
+ */
+int memcg_get_anon_usage(struct memcg_info *mi, unsigned int *anon_usage);
+
+/**
+ * @desc get memory.get usage_in_bytes from cgroup mi (this is value without swap)
+ * @return 0 if the value was correctly read
+ */
+int memcg_get_usage(struct memcg_info *mi, unsigned int *usage_in_bytes);
+
+/**
+ * @desc get PIDs of processes in mi cgroup, an allocated array must be provided
+ * @return 0 if pids were read and array filled
+ */
+int memcg_get_pids(struct memcg_info *mi, GArray *pids);
+
+#endif /*__MEMORY_COMMONL_H__*/
--- /dev/null
+/*
+ * 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 module-data.c
+ * @desc Module data features
+ **/
+
+#include "macro.h"
+#include "module-data.h"
+#include "trace.h"
+
+static struct shared_modules_data modules_data;
+
+struct shared_modules_data *get_shared_modules_data(void)
+{
+ return &modules_data;
+}
+
+void init_modules_arg(struct daemon_arg *darg)
+{
+ ret_msg_if(darg == NULL,
+ "Init modules argument failed\n");
+ modules_data.darg = darg;
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 module-data.h
+ * @desc Module data features
+ **/
+
+#ifndef __MODULE_DATA_HANDLE_H__
+#define __MODULE_DATA_HANDLE_H__
+
+#include "counter.h"
+#include "init.h"
+
+struct swap_module_data {
+ int swap_state; /* swap SWAP_ON/SWAP_OFF */
+};
+
+struct shared_modules_data {
+ struct counter_arg *carg;
+ struct daemon_arg *darg;
+ struct swap_module_data swap_data;
+};
+
+struct shared_modules_data *get_shared_modules_data(void);
+
+void init_modules_arg(struct daemon_arg *darg);
+
+#endif /* __MODULE_DATA_HANDLE_H__ */
--- /dev/null
+/*
+ * 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 module.c
+ * @desc Module helper functions
+ **/
+
+#include "macro.h"
+#include "module.h"
+#include "resourced.h"
+#include "trace.h"
+#include "edbus-handler.h"
+
+#include <glib.h>
+
+static GSList *modules_list;
+
+void add_module(const struct module_ops *module)
+{
+ ret_msg_if(!module, "Invalid module handler\n");
+ if (module->priority == MODULE_PRIORITY_HIGH)
+ modules_list = g_slist_prepend(modules_list, (gpointer)module);
+ else
+ modules_list = g_slist_append(modules_list, (gpointer)module);
+}
+
+void remove_module(const struct module_ops *module)
+{
+ modules_list = g_slist_remove(modules_list, (gpointer)module);
+}
+
+const struct module_ops *find_module(const char *name)
+{
+ GSList *iter;
+ const struct module_ops *module;
+
+ gslist_for_each_item(iter, modules_list) {
+ module = (struct module_ops *)iter->data;
+ if (!strcmp(module->name, name))
+ return module;
+ }
+ return NULL;
+}
+
+void modules_check_runtime_support(void UNUSED *data)
+{
+ GSList *iter, *next;
+ const struct module_ops *module;
+ int ret_code = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_safe(modules_list, iter, next, module) {
+ module = (const struct module_ops *)iter->data;
+ _D("check runtime support [%s] module\n", module->name);
+
+ if (!module->check_runtime_support)
+ continue;
+
+ ret_code = module->check_runtime_support((void *)module);
+ if (ret_code != RESOURCED_ERROR_NONE) {
+ _E("%s module check failed", module->name);
+ remove_module(module);
+ continue;
+ }
+ }
+}
+
+static void module_initcall_level(void *data, int priority)
+{
+ GSList *iter;
+ struct module_ops *module;
+ int ret_code = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, modules_list) {
+ module = (struct module_ops *)iter->data;
+ if (priority != MODULE_PRIORITY_ALL &&
+ module->priority != priority)
+ continue;
+ if (module->init && !module->initalized) {
+ _D("Initialized [%s] module\n", module->name);
+ ret_code = module->init(data);
+ module->initalized = MODULE_INITIALIZED;
+ }
+ if (ret_code < 0)
+ _E("Fail to initialize [%s] module\n", module->name);
+ }
+}
+
+void modules_init(void *data)
+{
+ module_initcall_level(data, MODULE_PRIORITY_ALL);
+}
+
+void modules_early_init(void *data)
+{
+ module_initcall_level(data, MODULE_PRIORITY_EARLY);
+}
+
+void modules_late_init(void *data)
+{
+ module_initcall_level(data, MODULE_PRIORITY_HIGH);
+ module_initcall_level(data, MODULE_PRIORITY_NORMAL);
+}
+
+void modules_exit(void *data)
+{
+ GSList *iter;
+ struct module_ops *module;
+ int ret_code = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, modules_list) {
+ module = (struct module_ops *)iter->data;
+ _D("Deinitialize [%s] module\n", module->name);
+ if (module->exit) {
+ ret_code = module->exit(data);
+ module->initalized = MODULE_NONINITIALIZED;
+ }
+ if (ret_code < 0)
+ _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);
+ }
+}
+
+static DBusMessage *edbus_list_active_modules_handler(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ struct module_ops *module;
+ GSList *list_iter;
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &array_iter)) {
+ _E("Failed to open DBus container");
+ goto finish;
+ }
+
+ gslist_for_each_item(list_iter, modules_list) {
+ module = (struct module_ops *)list_iter->data;
+
+ if (!dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_STRING,
+ &module->name)) {
+ _E("Failed to append string to DBus container");
+ goto finish;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &array_iter))
+ _E("Failed to close DBus container");
+
+finish:
+ return reply;
+}
+
+static struct edbus_method resourced_module_methods[] = {
+ { "ListActiveModuels", NULL, "as", edbus_list_active_modules_handler },
+ { NULL, NULL, NULL, NULL },
+ /* Add methods here */
+};
+
+int modules_add_methods(void)
+{
+ return edbus_add_methods(RESOURCED_DBUS_OBJECT_PATH,
+ resourced_module_methods,
+ ARRAY_SIZE(resourced_module_methods));
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 module.h
+ * @desc Module helper functions
+ **/
+
+#ifndef __MODULE_HANDLE_H__
+#define __MODULE_HANDLE_H__
+
+enum module_priority {
+ MODULE_PRIORITY_NORMAL,
+ MODULE_PRIORITY_HIGH,
+ MODULE_PRIORITY_EARLY,
+ MODULE_PRIORITY_ALL,
+};
+
+enum module_state {
+ MODULE_NONINITIALIZED,
+ MODULE_INITIALIZED,
+};
+
+
+struct module_ops {
+ enum module_priority priority;
+ enum module_state initalized;
+ const char *name;
+ int (*init) (void *data);
+ int (*exit) (void *data);
+ 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);
+void remove_module(const struct module_ops *module);
+
+void modules_check_runtime_support(void *data);
+void modules_init(void *data);
+void modules_early_init(void *data);
+void modules_late_init(void *data);
+void modules_exit(void *data);
+void modules_dump(FILE *fp, int mode);
+int modules_add_methods(void);
+
+const struct module_ops *find_module(const char *name);
+
+#endif /* __MODULE_HANDLE_H__ */
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "notifier.h"
+
+#include <resourced.h>
+#include <trace.h>
+#include <stdlib.h>
+#include <glib.h>
+
+struct resourced_notifier {
+ enum notifier_type status;
+ int (*func)(void *data);
+};
+
+static GSList *resourced_notifier_list;
+
+#define FIND_NOTIFIER(a, b, d, e, f) \
+ gslist_for_each(a, b, d) \
+ if (e == d->e && f == (d->f))
+
+int register_notifier(enum notifier_type status, int (*func)(void *data))
+{
+ GSList *n;
+ struct resourced_notifier *notifier;
+
+ _I("%d, %x", status, func);
+
+ if (!func) {
+ _E("invalid func address!");
+ return -EINVAL;
+ }
+
+ FIND_NOTIFIER(resourced_notifier_list, n, notifier, status, func) {
+ _E("function is already registered! [%d, %x]",
+ status, func);
+ return -EINVAL;
+ }
+
+ notifier = malloc(sizeof(struct resourced_notifier));
+ if (!notifier) {
+ _E("Fail to malloc for notifier!");
+ return -ENOMEM;
+ }
+
+ notifier->status = status;
+ notifier->func = func;
+
+ resourced_notifier_list = g_slist_append(resourced_notifier_list, notifier);
+
+ return 0;
+}
+
+int unregister_notifier(enum notifier_type status, int (*func)(void *data))
+{
+ GSList *n;
+ struct resourced_notifier *notifier;
+
+ if (!func) {
+ _E("invalid func address!");
+ return -EINVAL;
+ }
+
+ FIND_NOTIFIER(resourced_notifier_list, n, notifier, status, func) {
+ _I("[%d, %x]", status, func);
+ resourced_notifier_list = g_slist_remove(resourced_notifier_list, notifier);
+ free(notifier);
+ }
+
+ return 0;
+}
+
+void resourced_notify(enum notifier_type status, void *data)
+{
+ GSList *iter;
+ struct resourced_notifier *notifier;
+
+ gslist_for_each_item(iter, resourced_notifier_list) {
+ notifier = (struct resourced_notifier *)iter->data;
+ if (status == notifier->status) {
+ if (notifier->func)
+ notifier->func(data);
+ }
+ }
+}
+
+static int notifier_exit(void *data)
+{
+ GSList *iter;
+ /* Deinitialize in reverse order */
+ GSList *reverse_list = g_slist_reverse(resourced_notifier_list);
+ struct resourced_notifier *notifier;
+
+ gslist_for_each_item(iter, reverse_list) {
+ notifier = (struct resourced_notifier *)iter->data;
+ resourced_notifier_list = g_slist_remove(resourced_notifier_list, iter);
+ free(notifier);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops notifier_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "notifier",
+ .exit = notifier_exit,
+};
+
+MODULE_REGISTER(¬ifier_ops)
+
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+
+#ifndef __NOTIFIER_H__
+#define __NOTIFIER_H__
+
+enum notifier_type {
+ /*
+ * application status
+ */
+ RESOURCED_NOTIFIER_APP_LAUNCH,
+ RESOURCED_NOTIFIER_APP_RESUME,
+ RESOURCED_NOTIFIER_APP_FOREGRD,
+ RESOURCED_NOTIFIER_APP_BACKGRD,
+ RESOURCED_NOTIFIER_APP_BACKGRD_ACTIVE,
+ RESOURCED_NOTIFIER_APP_ACTIVE,
+ RESOURCED_NOTIFIER_APP_INACTIVE,
+ RESOURCED_NOTIFIER_APP_PRELAUNCH,
+ RESOURCED_NOTIFIER_APP_ANR,
+ /*
+ * start terminating application
+ */
+ RESOURCED_NOTIFIER_APP_TERMINATE_START,
+ /*
+ * finished application termination
+ */
+ RESOURCED_NOTIFIER_APP_TERMINATED,
+
+ /*
+ * suspend background application
+ */
+ RESOURCED_NOTIFIER_APP_WAKEUP,
+ RESOURCED_NOTIFIER_APP_SUSPEND_READY,
+ RESOURCED_NOTIFIER_APP_SUSPEND,
+
+ /*
+ * service status
+ */
+ RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+ RESOURCED_NOTIFIER_SERVICE_WAKEUP,
+
+ /*
+ * widget status
+ */
+ RESOURCED_NOTIFIER_WIDGET_FOREGRD,
+ RESOURCED_NOTIFIER_WIDGET_BACKGRD,
+
+ /*
+ * control resourced module
+ */
+ RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ RESOURCED_NOTIFIER_SWAP_START,
+ RESOURCED_NOTIFIER_SWAP_UNSET_LIMIT,
+ RESOURCED_NOTIFIER_SWAP_ACTIVATE,
+ RESOURCED_NOTIFIER_SWAP_COMPACT,
+ RESOURCED_NOTIFIER_LOGGING_START,
+ RESOURCED_NOTIFIER_LOGGING_WRITE,
+ RESOURCED_NOTIFIER_DATA_UPDATE,
+ RESOURCED_NOTIFIER_DATA_RESET,
+ RESOURCED_NOTIFIER_SYSTEM_SERVICE,
+ RESOURCED_NOTIFIER_CONTROL_EXCLUDE,
+
+ /*
+ * receive external event
+ */
+ RESOURCED_NOTIFIER_BOOTING_DONE,
+ RESOURCED_NOTIFIER_POWER_OFF,
+ RESOURCED_NOTIFIER_SYSTEMTIME_CHANGED,
+ RESOURCED_NOTIFIER_LOW_BATTERY,
+ RESOURCED_NOTIFIER_LCD_ON,
+ RESOURCED_NOTIFIER_LCD_OFF,
+
+ RESOURCED_NOTIFIER_MAX,
+};
+
+/*
+ * This is for internal callback method.
+ */
+int register_notifier(enum notifier_type status, int (*func)(void *data));
+int unregister_notifier(enum notifier_type status, int (*func)(void *data));
+void resourced_notify(enum notifier_type status, void *value);
+
+#endif /* __NOTIFIER_H__ */
--- /dev/null
+/*
+ * 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-common.h
+ * @desc proc common process
+ **/
+
+#ifndef __PROC_COMMON_H__
+#define __PROC_COMMON_H__
+
+#include <unistd.h>
+#include <glib.h>
+#include <string.h>
+
+#include "resourced.h"
+#include "const.h"
+#include "memory-common.h"
+
+typedef GSList *pid_list;
+
+enum application_type {
+ PROC_TYPE_NONE,
+ PROC_TYPE_READY,
+ PROC_TYPE_GUI,
+ PROC_TYPE_SERVICE,
+ PROC_TYPE_GROUP,
+ PROC_TYPE_WATCH,
+ PROC_TYPE_WIDGET,
+ PROC_TYPE_MAX,
+};
+
+enum proc_state {
+ PROC_STATE_DEFAULT,
+ PROC_STATE_FOREGROUND,
+ PROC_STATE_BACKGROUND,
+ PROC_STATE_SUSPEND_READY,
+ PROC_STATE_SUSPEND,
+};
+
+struct child_pid {
+ pid_t pid;
+};
+
+struct proc_status {
+ pid_t pid;
+ char* appid;
+ struct proc_app_info *pai;
+};
+
+enum proc_exclude_type {
+ PROC_INCLUDE,
+ PROC_EXCLUDE,
+};
+
+enum {
+ LCD_STATE_ON,
+ LCD_STATE_OFF,
+};
+
+enum proc_prelaunch_flags {
+ 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 */
+ PROC_DOWNLOADAPP = 0x08u, /* for monitoring disk usage about downloadable app */
+ PROC_SERVICEAPP = 0x10u, /* for distinguishing service app and ui app */
+ PROC_VIP_ATTRIBUTE = 0x20u, /* for giving VIP attribute about MDM app */
+ PROC_BGALLOW = 0x100u, /* for allowing background application */
+ PROC_BGCTRL_PLATFORM = 0x200u, /* for controlling background application by appfw */
+ PROC_BGCTRL_APP = 0x400u, /* for checking old version application */
+};
+
+/*
+ * BG categories in TIZEN 2.4
+ * Media : Playing audio, recoding, and streaming video in background
+ * Download : Downloading data with the Tizen download-manager API
+ * Background network : Processing general network operation
+ * in background (Sync-manager, IM / VOIP)
+ * Location: Processing location data in background
+ * Sensor : Processing context data from the sensors
+ * such as gesture, health (product)
+ * IOT communition & connectivity : Communicating between external
+ * devices in background such as WIFI, BT, SAP (only product)
+ * System : hidden category for system applications like home
+ */
+enum proc_background_category {
+ PROC_BG_NONE = 0x0,
+ PROC_BG_MEDIA = 0x1,
+ PROC_BG_DOWNLOAD = 0x2,
+ PROC_BG_NETWORK = 0x4,
+ PROC_BG_LOCATION = 0x8,
+ PROC_BG_SENSOR = 0x10,
+ PROC_BG_IOT = 0x20,
+ PROC_BG_SYSTEM = 0x40,
+};
+
+enum proc_lru_state {
+ PROC_FOREGROUND = -1,
+ PROC_ACTIVE = 0,
+ PROC_BACKGROUND = 1,
+ PROC_LRU_MAX = 15,
+};
+
+extern GSList *proc_app_list;
+
+struct proc_exclude {
+ pid_t pid;
+ enum proc_exclude_type type;
+};
+
+struct proc_program_info {
+ char *pkgname;
+ GSList *app_list;
+ GSList *svc_list;
+};
+
+struct proc_memory_state {
+ struct memcg_info *memcg_info;
+ int memcg_idx;
+ int oom_score_adj;
+};
+
+struct proc_app_info {
+ char *appid;
+ pid_t main_pid;
+ pid_list childs;
+ int proc_exclude;
+ int runtime_exclude;
+ int flags;
+ int lru_state;
+ int categories;
+ enum proc_state state;
+ enum application_type type;
+ struct resourced_appinfo *ai;
+ struct proc_program_info *program;
+ struct proc_memory_state memory;
+
+};
+
+int proc_get_freezer_status(void);
+
+struct proc_app_info *find_app_info(const pid_t pid);
+struct proc_app_info *find_app_info_by_appid(const char *appid);
+
+struct child_pid *new_pid_info(const pid_t pid);
+int proc_get_id_info(struct proc_status *ps, char **app_name, char **pkg_name);
+
+/**
+ * @desc set memory field in proc_app_info sturcture of selected pai, the data
+ * are used by lowmem module.
+ * @return void
+ */
+void proc_set_process_memory_state(struct proc_app_info *pai,
+ int memcg_idx, struct memcg_info *memcg_info, int oom_score_adj);
+
+int proc_get_appflag(const pid_t pid);
+
+static inline int equal_name_info(const char *id_a, const char *id_b)
+{
+ return !strcmp(id_a, id_b);
+}
+
+int proc_get_svc_state(struct proc_program_info *ppi);
+
+bool proc_check_lru_suspend(int val, int lru);
+
+enum proc_state proc_check_suspend_state(struct proc_app_info *pai);
+
+int proc_debug_enabled(void);
+
+#endif /* __PROC_COMMON_H__ */
--- /dev/null
+/*
+ * 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-cpu.c
+ * @desc: Helper functions for getting cpu stat and usage
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "proc-cpu.h"
+#include "trace.h"
+#include "util.h"
+
+#define BUF_MAX 1024
+
+resourced_ret_c proc_cpu_stat(struct cpu_stat *cs)
+{
+ FILE *fp;
+ int retval;
+ char buffer[BUF_MAX];
+
+ fp = fopen("/proc/stat", "r");
+ if (fp == NULL) {
+ _E("fopen -/proc/stat- faied");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ retval = fseek(fp, 0, SEEK_SET);
+ if (retval < 0) {
+ _E("fseek() failed");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!fgets(buffer, BUF_MAX, fp)) {
+ _E ("fgets() failed");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ retval = sscanf(buffer, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ &cs->cs[0], /* user */
+ &cs->cs[1], /* nice */
+ &cs->cs[2], /* system */
+ &cs->cs[3], /* idle */
+ &cs->cs[4], /* iowait */
+ &cs->cs[5], /* irq */
+ &cs->cs[6], /* softirq */
+ &cs->cs[7], /* steal */
+ &cs->cs[8], /* guest */
+ &cs->cs[9]); /* guest_nice */
+ if (retval < 4) { /* Atleast 4 fields is to be read */
+ _E("Error reading /proc/stat cpu field");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+double proc_cpu_usage(struct cpu_stat *cs1, struct cpu_stat *cs2)
+{
+ int i;
+ unsigned long long int total_tick_old, total_tick, diff_total_tick, diff_idle;
+ double cpu_usage, idlep;
+
+ /* first reading */
+ for (i = 0, total_tick_old = 0; i < PROC_STAT_MAX_FLDS; i++)
+ total_tick_old += cs1->cs[i];
+
+ /* second reading */
+ for (i = 0, total_tick = 0; i < PROC_STAT_MAX_FLDS; i++)
+ total_tick += cs2->cs[i];
+
+ /* Calculate CPU idle and used percentage */
+ diff_total_tick = NUM_DIFF(total_tick, total_tick_old);
+ diff_idle = NUM_DIFF(cs2->cs[3], cs1->cs[3]);
+ idlep = (diff_idle / (double)diff_total_tick) * 100;
+ cpu_usage = 100 - idlep;
+ return cpu_usage;
+}
--- /dev/null
+/*
+ * 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-cpu.h
+ * @desc Helper functions to get cpu stat and usage
+ */
+
+#ifndef _RESOURCED_PROC_CPU_H_
+#define _RESOURCED_PROC_CPU_H_
+
+#include "resourced.h"
+
+#define PROC_STAT_MAX_FLDS 10
+
+struct cpu_stat {
+ unsigned long long int cs[PROC_STAT_MAX_FLDS+1];
+};
+
+/**
+ * @desc reads cpu stat from /proc/stat
+ * @param cs - cpu stat read from /proc/stat
+ * @return negative value if error
+ */
+resourced_ret_c proc_cpu_stat(struct cpu_stat *cs);
+
+/**
+ * @desc computes cpu usage
+ * @param cs1- first cpu stat, cs2- second cpu stat
+ * @return cpu usage percentage
+ */
+double proc_cpu_usage(struct cpu_stat *cs1, struct cpu_stat *cs2);
+
+#endif /*_RESOURCED_PROC_CPU_H_*/
--- /dev/null
+/*
+ * 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 procfs.c
+ *
+ * @desc communicate with procfs in resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "macro.h"
+#include "util.h"
+#include "procfs.h"
+#include "proc-common.h"
+#include "lowmem-common.h"
+#include "module.h"
+
+#define PAGE_SIZE_KB 4
+
+static struct sys_node_table sys_node_tables[] = {
+ { SYS_VM_SHRINK_MEMORY, "/proc/sys/vm/shrink_memory", 1, 1 },
+ { SYS_VM_COMPACT_MEMORY, "/proc/sys/vm/compact_memory", 1, 1 },
+ { },
+};
+
+int proc_get_cmdline(pid_t pid, char *cmdline)
+{
+ char buf[PROC_BUF_MAX];
+ char cmdline_buf[PROC_NAME_MAX];
+ char *filename;
+ FILE *fp;
+
+ snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fgets(cmdline_buf, PROC_NAME_MAX-1, fp) == NULL) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fclose(fp);
+
+ filename = strrchr(cmdline_buf, '/');
+ if (filename == NULL)
+ filename = cmdline_buf;
+ else
+ filename = filename + 1;
+
+ strncpy(cmdline, filename, PROC_NAME_MAX-1);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+pid_t find_pid_from_cmdline(char *cmdline)
+{
+ pid_t pid = -1, foundpid = -1;
+ int ret = 0;
+ DIR *dp;
+ struct dirent dentry;
+ struct dirent *result;
+ char appname[PROC_NAME_MAX];
+
+ dp = opendir("/proc");
+ if (!dp) {
+ _E("BACKGRD MANAGE : fail to open /proc");
+ return RESOURCED_ERROR_FAIL;
+ }
+ while (!readdir_r(dp, &dentry, &result) && result != NULL) {
+ if (!isdigit(dentry.d_name[0]))
+ continue;
+
+ pid = atoi(dentry.d_name);
+ if (!pid)
+ continue;
+ ret = proc_get_cmdline(pid, appname);
+ if (ret == RESOURCED_ERROR_NONE) {
+ if (!strcmp(cmdline, appname)) {
+ foundpid = pid;
+ break;
+ }
+ }
+ }
+ closedir(dp);
+ return foundpid;
+}
+
+int proc_get_oom_score_adj(int pid, int *oom_score_adj)
+{
+ char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+ FILE *fp = NULL;
+
+ if (pid < 0)
+ return RESOURCED_ERROR_FAIL;
+
+ snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+ fp = fopen(buf, "r");
+
+ if (fp == NULL) {
+ _E("fopen %s failed", buf);
+ return RESOURCED_ERROR_FAIL;
+ }
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ (*oom_score_adj) = atoi(buf);
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_set_oom_score_adj(int pid, int oom_score_adj)
+{
+ FILE *fp;
+ struct lowmem_data_type lowmem_data;
+ static const struct module_ops *lowmem;
+ char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+
+ snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+ fp = fopen(buf, "r+");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fprintf(fp, "%d", oom_score_adj);
+ fclose(fp);
+
+ if(!lowmem) {
+ lowmem = find_module("lowmem");
+ if (!lowmem)
+ return RESOURCED_ERROR_FAIL;
+ }
+ if (lowmem && (oom_score_adj >= OOMADJ_SU)) {
+ lowmem_data.control_type = LOWMEM_MOVE_CGROUP;
+ lowmem_data.args[0] = (int)pid;
+ lowmem_data.args[1] = (int)oom_score_adj;
+ lowmem->control(&lowmem_data);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_label(pid_t pid, char *label)
+{
+ char buf[PROC_BUF_MAX];
+ FILE *fp;
+
+ snprintf(buf, sizeof(buf), "/proc/%d/attr/current", pid);
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fgets(label, PROC_NAME_MAX-1, fp) == NULL) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_mem_usage(pid_t pid, unsigned int *vmsize, unsigned int *vmrss)
+{
+ char buf[PROC_BUF_MAX];
+ char statm_buf[PROC_NAME_MAX];
+ unsigned int size, rss;
+ FILE *fp;
+
+
+ snprintf(buf, sizeof(buf), "/proc/%d/statm", pid);
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fgets(statm_buf, PROC_NAME_MAX-1, fp) == NULL) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fclose(fp);
+
+ if (sscanf(statm_buf, "%u %u", &size, &rss) < 2)
+ return RESOURCED_ERROR_FAIL;
+
+ if (vmsize != NULL)
+ *vmsize = size*PAGE_SIZE_KB;
+ if (vmrss != NULL)
+ *vmrss = rss*PAGE_SIZE_KB;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+unsigned int proc_get_mem_available(void)
+{
+ struct meminfo mi;
+ int r;
+ char buf[256];
+
+ r = proc_get_meminfo(&mi, MEMINFO_MASK_MEM_AVAILABLE);
+ if (r < 0) {
+ _E("Failed to get %s: %s",
+ meminfo_id_to_string(MEMINFO_ID_MEM_AVAILABLE),
+ strerror_r(-r, buf, sizeof(buf)));
+ return 0;
+ }
+
+ return KBYTE_TO_MBYTE(mi.value[MEMINFO_ID_MEM_AVAILABLE]);
+}
+
+unsigned int proc_get_swap_free(void)
+{
+ struct meminfo mi;
+ int r;
+
+ r = proc_get_meminfo(&mi, MEMINFO_MASK_SWAP_FREE);
+ if (r < 0) {
+ _E("Failed to get %s: %s",
+ meminfo_id_to_string(MEMINFO_ID_SWAP_FREE),
+ strerror(-r));
+ return 0;
+ }
+
+ return mi.value[MEMINFO_ID_SWAP_FREE];
+}
+
+int proc_get_cpu_time(pid_t pid, unsigned long *utime,
+ unsigned long *stime)
+{
+ char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
+ _cleanup_fclose_ FILE *fp = NULL;
+
+ assert(utime != NULL);
+ assert(stime != NULL);
+
+ snprintf(proc_path, sizeof(proc_path), PROC_STAT_PATH, pid);
+ fp = fopen(proc_path, "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (fscanf(fp, "%lu %lu", utime, stime) < 1) {
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+unsigned int proc_get_cpu_number(void)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+ int cpu = 0;
+
+ fp = fopen("/proc/cpuinfo", "r");
+
+ if (!fp) {
+ _E("/proc/cpuinfo open failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ while (fgets(buf, PATH_MAX, fp) != NULL) {
+ if (!strncmp(buf, "processor", 9))
+ cpu++;
+ }
+
+ fclose(fp);
+ return cpu;
+}
+
+int proc_get_exepath(pid_t pid, char *buf, int len)
+{
+ char path[PROC_BUF_MAX];
+ int ret = 0;
+
+ snprintf(path, sizeof(path), "/proc/%d/exe", pid);
+ ret = readlink(path, buf, len-1);
+ if (ret > 0)
+ buf[ret] = '\0';
+ else
+ buf[0] = '\0';
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_get_data(char *path, char *buf, int len)
+{
+ _cleanup_close_ int fd = -1;
+ int ret;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return RESOURCED_ERROR_FAIL;
+
+ ret = read(fd, buf, len-1);
+ if (ret < 0) {
+ buf[0] = '\0';
+ return RESOURCED_ERROR_FAIL;
+ }
+ buf[ret] = '\0';
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_raw_cmdline(pid_t pid, char *buf, int len)
+{
+ char path[PROC_BUF_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
+ return proc_get_data(path, buf, len);
+}
+
+int proc_get_stat(pid_t pid, char *buf, int len)
+{
+ char path[PROC_BUF_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/stat", pid);
+ return proc_get_data(path, buf, len);
+}
+
+int proc_get_status(pid_t pid, char *buf, int len)
+{
+ char path[PROC_BUF_MAX];
+ snprintf(path, sizeof(path), "/proc/%d/status", pid);
+ return proc_get_data(path, buf, len);
+}
+
+int proc_sys_node_trigger(enum sys_node_id sys_node_id)
+{
+ FILE *fp = NULL;
+
+ if (sys_node_id >= ARRAY_SIZE(sys_node_tables)) {
+ _E("sys_node_id[%d] is out of range.\n", sys_node_id);
+ return RESOURCED_ERROR_FAIL;
+ }
+ if (!sys_node_tables[sys_node_id].valid) {
+ _E("sys_node_id[%d] is not valid.\n", sys_node_id);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* open and check if the path exists, else return fail */
+ fp = fopen(sys_node_tables[sys_node_id].path, "w");
+ if (fp == NULL) {
+ _E("Failed to open: %s: %s\n",
+ sys_node_tables[sys_node_id].path, strerror(errno));
+ sys_node_tables[sys_node_id].valid = 0;
+ return RESOURCED_ERROR_FAIL;
+ }
+ fputc(sys_node_tables[sys_node_id].value, fp);
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+static const char* const meminfo_string_lookup[MEMINFO_ID_MAX] = {
+ [MEMINFO_ID_MEM_TOTAL] = "MemTotal",
+ [MEMINFO_ID_MEM_FREE] = "MemFree",
+ [MEMINFO_ID_MEM_AVAILABLE] = "MemAvailable",
+ [MEMINFO_ID_BUFFERS] = "Buffers",
+ [MEMINFO_ID_CACHED] = "Cached",
+ [MEMINFO_ID_SWAP_CACHED] = "SwapCached",
+ [MEMINFO_ID_ACTIVE] = "Active",
+ [MEMINFO_ID_INACTIVE] = "Inactive",
+ [MEMINFO_ID_ACTIVE_ANON] = "Active(anon)",
+ [MEMINFO_ID_INACTIVE_ANON] = "Inactive(anon)",
+ [MEMINFO_ID_ACTIVE_FILE] = "Active(file)",
+ [MEMINFO_ID_INACTIVE_FILE] = "Inactive(file)",
+ [MEMINFO_ID_UNEVICTABLE] = "Unevictable",
+ [MEMINFO_ID_MLOCKED] = "Mlocked",
+ [MEMINFO_ID_HIGH_TOTAL] = "HighTotal",
+ [MEMINFO_ID_HIGH_FREE] = "HighFree",
+ [MEMINFO_ID_LOW_TOTAL] = "LowTotal",
+ [MEMINFO_ID_LOW_FREE] = "LowFree",
+ [MEMINFO_ID_SWAP_TOTAL] = "SwapTotal",
+ [MEMINFO_ID_SWAP_FREE] = "SwapFree",
+ [MEMINFO_ID_DIRTY] = "Dirty",
+ [MEMINFO_ID_WRITEBACK] = "Writeback",
+ [MEMINFO_ID_ANON_PAGES] = "AnonPages",
+ [MEMINFO_ID_MAPPED] = "Mapped",
+ [MEMINFO_ID_SHMEM] = "Shmem",
+ [MEMINFO_ID_SLAB] = "Slab",
+ [MEMINFO_ID_SRECLAIMABLE] = "SReclaimable",
+ [MEMINFO_ID_SUNRECLAIM] = "SUnreclaim",
+ [MEMINFO_ID_KERNEL_STACK] = "KernelStack",
+ [MEMINFO_ID_PAGE_TABLES] = "PageTables",
+ [MEMINFO_ID_NFS_UNSTABLE] = "NFS_Unstable",
+ [MEMINFO_ID_BOUNCE] = "Bounce",
+ [MEMINFO_ID_WRITEBACK_TMP] = "WritebackTmp",
+ [MEMINFO_ID_COMMIT_LIMIT] = "CommitLimit",
+ [MEMINFO_ID_COMMITTED_AS] = "Committed_AS",
+ [MEMINFO_ID_VMALLOC_TOTAL] = "VmallocTotal",
+ [MEMINFO_ID_VMALLOC_USED] = "VmallocUsed",
+ [MEMINFO_ID_VMALLOC_CHUNK] = "VmallocChunk",
+};
+
+const char *meminfo_id_to_string(enum meminfo_id id)
+{
+ assert(id >= 0 && id < MEMINFO_ID_MAX);
+
+ return meminfo_string_lookup[id];
+}
+
+int proc_get_meminfo(struct meminfo *mi, enum meminfo_mask mask)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ enum meminfo_mask remain_mask = mask;
+ char buf[LINE_MAX];
+
+ assert(mi);
+
+ memset(mi, 0x0, sizeof(struct meminfo));
+
+ f = fopen("/proc/meminfo", "r");
+ if (!f)
+ return -errno;
+
+ if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE)
+ remain_mask |= (MEMINFO_MASK_MEM_FREE |
+ MEMINFO_MASK_CACHED);
+
+ while (remain_mask) {
+ _cleanup_free_ char *k = NULL;
+ unsigned int v = 0;
+ enum meminfo_id id;
+ size_t l;
+
+ if (!fgets(buf, sizeof(buf), f)) {
+ if (ferror(f))
+ return -errno;
+ break;
+ }
+
+ l = strcspn(buf, ":");
+ if (!l)
+ break;
+
+ k = strndup(buf, l);
+ if (!k)
+ return -ENOMEM;
+
+ id = meminfo_string_to_id(k);
+ if (id < 0 || id >= MEMINFO_ID_MAX)
+ continue;
+
+ if (!(remain_mask & (1ULL << id)))
+ continue;
+
+ remain_mask &= ~((1ULL << id));
+
+ if (sscanf(buf + l + 1, "%d", &v) != 1)
+ break;
+
+ mi->value[id] = v;
+ }
+
+ if (remain_mask & MEMINFO_MASK_MEM_AVAILABLE) {
+ mi->value[MEMINFO_ID_MEM_AVAILABLE] =
+ mi->value[MEMINFO_ID_MEM_FREE]
+ + mi->value[MEMINFO_ID_CACHED];
+
+ remain_mask &= ~MEMINFO_MASK_MEM_AVAILABLE;
+ }
+
+ if (remain_mask) {
+ enum meminfo_id i;
+
+ for (i = 0; i < MEMINFO_ID_MAX; i++)
+ if (remain_mask & (1 << i))
+ _E("Failed to get meminfo: '%s'",
+ meminfo_id_to_string(i));
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#ifndef __PROCFS_H__
+#define __PROCFS_H__
+
+#include <resourced.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#define OOMADJ_DISABLE (-1000)
+#define OOMADJ_SERVICE_MIN (-900)
+#define OOMADJ_SU (0)
+#define OOMADJ_INIT (100)
+#define OOMADJ_FOREGRD_LOCKED (150)
+#define OOMADJ_FOREGRD_UNLOCKED (200)
+#define OOMADJ_BACKGRD_PERCEPTIBLE (230)
+#define OOMADJ_BACKGRD_LOCKED (250)
+#define OOMADJ_FAVORITE (270)
+#define OOMADJ_BACKGRD_UNLOCKED (300)
+#define OOMADJ_APP_LIMIT OOMADJ_INIT
+#define OOMADJ_APP_MAX (990)
+#define OOMADJ_APP_INCREASE (30)
+
+/* OOMADJ_SERVICE_DEFAULT is default value for processes PROC_TYPE_SERVICE */
+#define OOMADJ_SERVICE_GAP (10)
+#define OOMADJ_SERVICE_DEFAULT (OOMADJ_BACKGRD_LOCKED - OOMADJ_SERVICE_GAP)
+
+/*
+ * OOMADJ_PREVIOUS_DEFAULT is default value for processes that are
+ * moved out from foreground cgroup ( >= OOMADJ_BACKGRD_PERCEPTIBLE)
+ * but being in a state before background cgroup ( >= OOMADJ_BACKGRD_UNLOCKED).
+ * In the middle it is possible to have process in favorite cgroup (== OOMADJ_FAVORITE).
+ */
+#define OOMADJ_PREVIOUS_GAP (10)
+#define OOMADJ_PREVIOUS_DEFAULT (OOMADJ_BACKGRD_LOCKED - OOMADJ_PREVIOUS_GAP)
+#define OOMADJ_PREVIOUS_FOREGRD (OOMADJ_FOREGRD_UNLOCKED - OOMADJ_PREVIOUS_GAP)
+#define OOMADJ_PREVIOUS_BACKGRD (OOMADJ_BACKGRD_UNLOCKED - OOMADJ_PREVIOUS_GAP)
+
+
+#define PROC_OOM_SCORE_ADJ_PATH "/proc/%d/oom_score_adj"
+#define PROC_STAT_PATH "/proc/%d/stat"
+
+enum meminfo_id {
+ MEMINFO_ID_INVALID = -1,
+ MEMINFO_ID_MEM_TOTAL = 0,
+ MEMINFO_ID_MEM_FREE,
+ MEMINFO_ID_MEM_AVAILABLE,
+ MEMINFO_ID_BUFFERS,
+ MEMINFO_ID_CACHED,
+ MEMINFO_ID_SWAP_CACHED,
+ MEMINFO_ID_ACTIVE,
+ MEMINFO_ID_INACTIVE,
+ MEMINFO_ID_ACTIVE_ANON,
+ MEMINFO_ID_INACTIVE_ANON,
+ MEMINFO_ID_ACTIVE_FILE,
+ MEMINFO_ID_INACTIVE_FILE,
+ MEMINFO_ID_UNEVICTABLE,
+ MEMINFO_ID_MLOCKED,
+ MEMINFO_ID_HIGH_TOTAL,
+ MEMINFO_ID_HIGH_FREE,
+ MEMINFO_ID_LOW_TOTAL,
+ MEMINFO_ID_LOW_FREE,
+ MEMINFO_ID_SWAP_TOTAL,
+ MEMINFO_ID_SWAP_FREE,
+ MEMINFO_ID_DIRTY,
+ MEMINFO_ID_WRITEBACK,
+ MEMINFO_ID_ANON_PAGES,
+ MEMINFO_ID_MAPPED,
+ MEMINFO_ID_SHMEM,
+ MEMINFO_ID_SLAB,
+ MEMINFO_ID_SRECLAIMABLE,
+ MEMINFO_ID_SUNRECLAIM,
+ MEMINFO_ID_KERNEL_STACK,
+ MEMINFO_ID_PAGE_TABLES,
+ MEMINFO_ID_NFS_UNSTABLE,
+ MEMINFO_ID_BOUNCE,
+ MEMINFO_ID_WRITEBACK_TMP,
+ MEMINFO_ID_COMMIT_LIMIT,
+ MEMINFO_ID_COMMITTED_AS,
+ MEMINFO_ID_VMALLOC_TOTAL,
+ MEMINFO_ID_VMALLOC_USED,
+ MEMINFO_ID_VMALLOC_CHUNK,
+ MEMINFO_ID_MAX,
+};
+
+enum meminfo_mask {
+ MEMINFO_MASK_MEM_TOTAL = 1ULL << MEMINFO_ID_MEM_TOTAL,
+ MEMINFO_MASK_MEM_FREE = 1ULL << MEMINFO_ID_MEM_FREE,
+ MEMINFO_MASK_MEM_AVAILABLE = 1ULL << MEMINFO_ID_MEM_AVAILABLE,
+ MEMINFO_MASK_BUFFERS = 1ULL << MEMINFO_ID_BUFFERS,
+ MEMINFO_MASK_CACHED = 1ULL << MEMINFO_ID_CACHED,
+ MEMINFO_MASK_SWAP_CACHED = 1ULL << MEMINFO_ID_SWAP_CACHED,
+ MEMINFO_MASK_ACTIVE = 1ULL << MEMINFO_ID_ACTIVE,
+ MEMINFO_MASK_INACTIVE = 1ULL << MEMINFO_ID_INACTIVE,
+ MEMINFO_MASK_ACTIVE_ANON = 1ULL << MEMINFO_ID_ACTIVE_ANON,
+ MEMINFO_MASK_INACTIVE_ANON = 1ULL << MEMINFO_ID_INACTIVE_ANON,
+ MEMINFO_MASK_ACTIVE_FILE = 1ULL << MEMINFO_ID_ACTIVE_FILE,
+ MEMINFO_MASK_INACTIVE_FILE = 1ULL << MEMINFO_ID_INACTIVE_FILE,
+ MEMINFO_MASK_UNEVICTABLE = 1ULL << MEMINFO_ID_UNEVICTABLE,
+ MEMINFO_MASK_MLOCKED = 1ULL << MEMINFO_ID_MLOCKED,
+ MEMINFO_MASK_HIGH_TOTAL = 1ULL << MEMINFO_ID_HIGH_TOTAL,
+ MEMINFO_MASK_HIGH_FREE = 1ULL << MEMINFO_ID_HIGH_FREE,
+ MEMINFO_MASK_LOW_TOTAL = 1ULL << MEMINFO_ID_LOW_TOTAL,
+ MEMINFO_MASK_LOW_FREE = 1ULL << MEMINFO_ID_LOW_FREE,
+ MEMINFO_MASK_SWAP_TOTAL = 1ULL << MEMINFO_ID_SWAP_TOTAL,
+ MEMINFO_MASK_SWAP_FREE = 1ULL << MEMINFO_ID_SWAP_FREE,
+ MEMINFO_MASK_DIRTY = 1ULL << MEMINFO_ID_DIRTY,
+ MEMINFO_MASK_WRITEBACK = 1ULL << MEMINFO_ID_WRITEBACK,
+ MEMINFO_MASK_ANON_PAGES = 1ULL << MEMINFO_ID_ANON_PAGES,
+ MEMINFO_MASK_MAPPED = 1ULL << MEMINFO_ID_MAPPED,
+ MEMINFO_MASK_SHMEM = 1ULL << MEMINFO_ID_SHMEM,
+ MEMINFO_MASK_SLAB = 1ULL << MEMINFO_ID_SLAB,
+ MEMINFO_MASK_SRECLAIMABLE = 1ULL << MEMINFO_ID_SRECLAIMABLE,
+ MEMINFO_MASK_SUNRECLAIM = 1ULL << MEMINFO_ID_SUNRECLAIM,
+ MEMINFO_MASK_KERNEL_STACK = 1ULL << MEMINFO_ID_KERNEL_STACK,
+ MEMINFO_MASK_PAGE_TABLES = 1ULL << MEMINFO_ID_PAGE_TABLES,
+ MEMINFO_MASK_NFS_UNSTABLE = 1ULL << MEMINFO_ID_NFS_UNSTABLE,
+ MEMINFO_MASK_BOUNCE = 1ULL << MEMINFO_ID_BOUNCE,
+ MEMINFO_MASK_WRITEBACK_TMP = 1ULL << MEMINFO_ID_WRITEBACK_TMP,
+ MEMINFO_MASK_COMMIT_LIMIT = 1ULL << MEMINFO_ID_COMMIT_LIMIT,
+ MEMINFO_MASK_COMMITTED_AS = 1ULL << MEMINFO_ID_COMMITTED_AS,
+ MEMINFO_MASK_VMALLOC_TOTAL = 1ULL << MEMINFO_ID_VMALLOC_TOTAL,
+ MEMINFO_MASK_VMALLOC_USED = 1ULL << MEMINFO_ID_VMALLOC_USED,
+ MEMINFO_MASK_VMALLOC_CHUNK = 1ULL << MEMINFO_ID_VMALLOC_CHUNK,
+ MEMINFO_MASK_ALL = (1ULL << MEMINFO_ID_MAX) - 1,
+};
+
+struct meminfo_mapping {
+ const char *name;
+ enum meminfo_id id;
+};
+typedef struct meminfo_mapping meminfo_mapping;
+
+const meminfo_mapping *meminfo_mapping_lookup(const char *str, unsigned int len);
+
+static inline enum meminfo_id meminfo_string_to_id(const char *str)
+{
+ const struct meminfo_mapping *i;
+
+ assert(str);
+ i = meminfo_mapping_lookup(str, strlen(str));
+ return i ? i->id : MEMINFO_ID_INVALID;
+}
+
+const char *meminfo_id_to_string(enum meminfo_id);
+
+struct meminfo {
+ unsigned int value[MEMINFO_ID_MAX];
+};
+
+/**
+ * @desc get info corresponding size(kB) from /proc/meminfo
+ * @note given meminfo struct is set all zero before filled
+ * @return 0 on success, return negative error code on fail.
+ */
+int proc_get_meminfo(struct meminfo *mi, enum meminfo_mask mask);
+
+/*
+ * This interface is required by proc_sys_node_trigger(...)
+ */
+enum sys_node_id {
+ SYS_VM_SHRINK_MEMORY,
+ SYS_VM_COMPACT_MEMORY,
+};
+
+/*
+ * Here,
+ * @path is /proc/sys/vm/{shrink,compact}_memory
+ * @value is always 1
+ * @valid - indicates whether the node is present in kernel or not
+ */
+struct sys_node_table {
+ enum sys_node_id sys_node_id;
+ const char *path;
+ int value;
+ int valid;
+};
+
+/**
+ * @desc get command line from /proc/{pid}/cmdline
+ * @return negative value if error
+ */
+int proc_get_cmdline(pid_t pid, char *cmdline);
+
+/**
+ * @desc find pid with /proc/{pid}/cmdline
+ * it returns first entry when many pids have same cmdline
+ * @return negative value if error
+ */
+pid_t find_pid_from_cmdline(char *cmdline);
+
+/**
+ * @desc get oom score adj value from /proc/{pid}/oom_score_adj
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_oom_score_adj(int pid, int *oom_score_adj);
+
+/**
+ * @desc set oom score adj value to /proc/{pid}/oom_score_adj
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_set_oom_score_adj(int pid, int oom_score_adj);
+
+/**
+ * @desc get smack subject label from /proc/{pid}/attr/current
+ * this label can indicate package name about child processes
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_label(pid_t pid, char *label);
+
+/**
+ * @desc get VmSize and VmRSS from /proc/{pid}/statm file.
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_mem_usage(pid_t pid, unsigned int *vmsize, unsigned int *vmrss);
+
+/**
+ * @desc get MemAvaliable from /proc/meminfo or calcuate it by MemFree+Cached
+ * @return 0 if the values can't be read or the avaliable memory value
+ */
+unsigned int proc_get_mem_available(void);
+
+/**
+ * @desc get SwapFree from /proc/meminfo
+ * @return 0 if the values can't be read or the free swap memory
+ */
+unsigned int proc_get_swap_free(void);
+
+/**
+ * @desc get number of CPUs from /proc/cpuinfo
+ * @return 0 if the number can't be found or number of CPUs
+ */
+unsigned int proc_get_cpu_number(void);
+
+/**
+ * @desc get utime and stime from /proc/{pid}/stat file.
+ * @return negative value if error or pid doesn't exist
+ */
+int proc_get_cpu_time(pid_t pid, unsigned long *utime, unsigned long *stime);
+
+/**
+ * @desc get command line from /proc/{pid}/cmdline without any truncation
+ * @return negative value if error
+ */
+int proc_get_raw_cmdline(pid_t pid, char *buf, int len);
+
+/**
+ * @desc get symblolic link about /proc/{pid}/exe
+ * @return negative value if error
+ */
+int proc_get_exepath(pid_t pid, char *buf, int len);
+
+/**
+ * @desc get stat from /proc/{pid}/stat
+ * @return negative value if error
+ */
+int proc_get_stat(pid_t pid, char *buf, int len);
+
+/**
+ * @desc get status from /proc/{pid}/status
+ * @return negative value if error
+ */
+int proc_get_status(pid_t pid, char *buf, int len);
+
+/**
+ * @desc invoke shrink_memory or compact_memory vm parameter.
+ * @return none
+ */
+int proc_sys_node_trigger(enum sys_node_id sys_node_id);
+
+#endif /*__PROCFS_H__*/
--- /dev/null
+%{
+#include "smaps.h"
+%}
+smap_mapping;
+%language=ANSI-C
+%define slot-name name
+%define hash-function-name smap_mapping_hash
+%define lookup-function-name smap_mapping_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+AnonHugePages, SMAPS_ID_ANON_HUGE_PAGES
+Anonymous, SMAPS_ID_ANONYMOUS
+KernelPageSize, SMAPS_ID_KERNEL_PAGE_SIZE
+Locked, SMAPS_ID_LOCKED
+MMUPageSize, SMAPS_ID_MMU_PAGE_SIZE
+PSwap, SMAPS_ID_PSWAP
+Private_Clean, SMAPS_ID_PRIVATE_CLEAN
+Private_Dirty, SMAPS_ID_PRIVATE_DIRTY
+Pss, SMAPS_ID_PSS
+Referenced, SMAPS_ID_REFERENCED
+Rss, SMAPS_ID_RSS
+Shared_Clean, SMAPS_ID_SHARED_CLEAN
+Shared_Dirty, SMAPS_ID_SHARED_DIRTY
+Size, SMAPS_ID_SIZE
+Swap, SMAPS_ID_SWAP
--- /dev/null
+/*
+ * 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 smaps.c
+ * @desc get smaps info of process
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "util.h"
+#include "smaps.h"
+
+static const char* const smaps_string_lookup[SMAPS_ID_MAX] = {
+ [SMAPS_ID_ANON_HUGE_PAGES] = "AnonHugePages",
+ [SMAPS_ID_ANONYMOUS] = "Anonymous",
+ [SMAPS_ID_KERNEL_PAGE_SIZE] = "KernelPageSize",
+ [SMAPS_ID_LOCKED] = "Locked",
+ [SMAPS_ID_MMU_PAGE_SIZE] = "MMUPageSize",
+ [SMAPS_ID_PSWAP] = "PSwap",
+ [SMAPS_ID_PRIVATE_CLEAN] = "Private_Clean",
+ [SMAPS_ID_PRIVATE_DIRTY] = "Private_Dirty",
+ [SMAPS_ID_PSS] = "Pss",
+ [SMAPS_ID_REFERENCED] = "Referenced",
+ [SMAPS_ID_RSS] = "Rss",
+ [SMAPS_ID_SHARED_CLEAN] = "Shared_Clean",
+ [SMAPS_ID_SHARED_DIRTY] = "Shared_Dirty",
+ [SMAPS_ID_SIZE] = "Size",
+ [SMAPS_ID_SWAP] = "Swap",
+};
+
+static void smap_free(struct smap *map)
+{
+ if (!map)
+ return;
+
+ if (map->mode)
+ free(map->mode);
+
+ if (map->name)
+ free(map->name);
+
+ free(map);
+}
+
+void smaps_free(struct smaps *maps)
+{
+ int i;
+
+ if (!maps)
+ return;
+
+ for (i = 0; i < maps->n_map; i++)
+ smap_free(maps->maps[i]);
+
+ free(maps->maps);
+ free(maps);
+}
+
+const char *smap_id_to_string(enum smap_id id)
+{
+ assert(id >= 0 && id < SMAPS_ID_MAX);
+
+ return smaps_string_lookup[id];
+}
+
+static int add_smap_to_smaps(struct smaps *maps, struct smap *map)
+{
+ int i;
+
+ assert(maps);
+ assert(map);
+
+ maps->n_map++;
+
+ maps->maps = (struct smap **)realloc(
+ maps->maps,
+ sizeof(struct smap *) * maps->n_map);
+ if (!maps->maps)
+ return -ENOMEM;
+
+ maps->maps[maps->n_map - 1] = map;
+
+ for (i = 0; i < SMAPS_ID_MAX; i++)
+ maps->sum[i] += map->value[i];
+
+ return 0;
+}
+
+int smaps_get(pid_t pid, struct smaps **maps, enum smap_mask mask)
+{
+ _cleanup_free_ char *path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ struct smaps *m = NULL;
+ char buf[LINE_MAX];
+ bool get_line = true;
+ int r;
+
+ assert(maps);
+
+ r = asprintf(&path, "/proc/%d/smaps", pid);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = access(path, F_OK);
+ if (r < 0)
+ return -errno;
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ m = new0(struct smaps, 1);
+ if (!m)
+ return -ENOMEM;
+
+ for (;;) {
+ struct smap *map = NULL;
+ int n;
+
+ if (get_line && !fgets(buf, sizeof(buf), f)) {
+ if (ferror(f)) {
+ r = -errno;
+ goto on_error;
+ }
+ break;
+ } else
+ get_line = true;
+
+ map = new0(struct smap, 1);
+ if (!map) {
+ r = -errno;
+ goto on_error;
+ }
+
+ n = sscanf(buf, "%x-%x %ms %*s %*s %*s %ms",
+ &map->start, &map->end, &map->mode, &map->name);
+
+ if (n == 3 && !map->name)
+ map->name = strdup("[anon]");
+ else if (n != 4) {
+ free(map);
+ r = -EINVAL;
+ goto on_error;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *k = NULL;
+ unsigned int v = 0;
+ enum smap_id id;
+ size_t l;
+
+ if (!fgets(buf, sizeof(buf), f)) {
+ if (ferror(f)) {
+ r = -errno;
+ goto on_error;
+ }
+ break;
+ }
+
+ if ((*buf >= '0' && *buf <= '9') ||
+ (*buf >= 'a' && *buf <= 'f')) {
+ get_line = false;
+ break;
+ }
+
+ l = strcspn(buf, ":");
+ if (!l)
+ break;
+
+ k = strndup(buf, l);
+ if (!k) {
+ r = -ENOMEM;
+ smap_free(map);
+ goto on_error;
+ }
+
+ id = smap_string_to_id(k);
+ if (id < 0 || id >= SMAPS_ID_MAX)
+ continue;
+
+ if (!(mask & (1 << id)))
+ continue;
+
+ if (sscanf(buf + l + 1, "%d kB", &v) != 1)
+ break;
+
+ map->value[id] = v;
+ }
+
+ r = add_smap_to_smaps(m, map);
+ if (r < 0)
+ goto on_error;
+ }
+
+ *maps = m;
+
+ return 0;
+
+on_error:
+ smaps_free(m);
+ return r;
+}
--- /dev/null
+/*
+ * 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 smaps.h
+ * @desc /proc/{PID}/smaps info loopup util
+ */
+
+#ifndef _RESOURCED_SMAPS_H_
+#define _RESOURCED_SMAPS_H_
+
+#include <assert.h>
+#include <string.h>
+
+#include "util.h"
+
+enum smap_id {
+ SMAPS_ID_INVALID = -1,
+ SMAPS_ID_ANON_HUGE_PAGES = 0,
+ SMAPS_ID_ANONYMOUS,
+ SMAPS_ID_KERNEL_PAGE_SIZE,
+ SMAPS_ID_LOCKED,
+ SMAPS_ID_MMU_PAGE_SIZE,
+ SMAPS_ID_PSWAP,
+ SMAPS_ID_PRIVATE_CLEAN,
+ SMAPS_ID_PRIVATE_DIRTY,
+ SMAPS_ID_PSS,
+ SMAPS_ID_REFERENCED,
+ SMAPS_ID_RSS,
+ SMAPS_ID_SHARED_CLEAN,
+ SMAPS_ID_SHARED_DIRTY,
+ SMAPS_ID_SIZE,
+ SMAPS_ID_SWAP,
+ SMAPS_ID_MAX,
+};
+
+enum smap_mask {
+ SMAPS_MASK_ANON_HUGE_PAGES = 1 << SMAPS_ID_ANON_HUGE_PAGES,
+ SMAPS_MASK_ANONYMOUS = 1 << SMAPS_ID_ANONYMOUS,
+ SMAPS_MASK_KERNEL_PAGE_SIZE = 1 << SMAPS_ID_KERNEL_PAGE_SIZE,
+ SMAPS_MASK_LOCKED = 1 << SMAPS_ID_LOCKED,
+ SMAPS_MASK_MMU_PAGE_SIZE = 1 << SMAPS_ID_MMU_PAGE_SIZE,
+ SMAPS_MASK_PSWAP = 1 << SMAPS_ID_PSWAP,
+ SMAPS_MASK_PRIVATE_CLEAN = 1 << SMAPS_ID_PRIVATE_CLEAN,
+ SMAPS_MASK_PRIVATE_DIRTY = 1 << SMAPS_ID_PRIVATE_DIRTY,
+ SMAPS_MASK_PSS = 1 << SMAPS_ID_PSS,
+ SMAPS_MASK_REFERENCED = 1 << SMAPS_ID_REFERENCED,
+ SMAPS_MASK_RSS = 1 << SMAPS_ID_RSS,
+ SMAPS_MASK_SHARED_CLEAN = 1 << SMAPS_ID_SHARED_CLEAN,
+ SMAPS_MASK_SHARED_DIRTY = 1 << SMAPS_ID_SHARED_DIRTY,
+ SMAPS_MASK_SIZE = 1 << SMAPS_ID_SIZE,
+ SMAPS_MASK_SWAP = 1 << SMAPS_ID_SWAP,
+ SMAPS_MASK_ALL = (1 << SMAPS_ID_MAX) - 1,
+};
+
+#define SMAPS_MASK_DEFAULT \
+ (SMAPS_MASK_SIZE | \
+ SMAPS_MASK_RSS | \
+ SMAPS_MASK_PSS | \
+ SMAPS_MASK_SHARED_CLEAN | \
+ SMAPS_MASK_SHARED_DIRTY | \
+ SMAPS_MASK_PRIVATE_CLEAN | \
+ SMAPS_MASK_PRIVATE_DIRTY | \
+ SMAPS_MASK_SWAP | \
+ SMAPS_MASK_PSWAP)
+
+struct smap_mapping {
+ const char* name;
+ enum smap_id id;
+};
+typedef struct smap_mapping smap_mapping;
+
+const smap_mapping *smap_mapping_lookup(const char *str, unsigned int len);
+
+static inline enum smap_id smap_string_to_id(const char *str)
+{
+ const struct smap_mapping *m;
+
+ assert(str);
+ m = smap_mapping_lookup(str,
+ strlen(str));
+ return m ? m->id : SMAPS_ID_INVALID;
+}
+
+const char *smap_id_to_string(enum smap_id);
+
+struct smap {
+ unsigned int start;
+ unsigned int end;
+ char *mode;
+ char *name;
+ unsigned int value[SMAPS_ID_MAX];
+};
+
+struct smaps {
+ unsigned int sum[SMAPS_ID_MAX];
+ int n_map;
+ struct smap **maps;
+};
+
+void smaps_free(struct smaps *maps);
+int smaps_get(pid_t pid, struct smaps **maps, enum smap_mask mask);
+
+static inline void smaps_freep(struct smaps **maps)
+{
+ if (*maps)
+ smaps_free(*maps);
+}
+
+#define _cleanup_smaps_free_ _cleanup_ (smaps_freep)
+
+#endif /* _RESOURCED_SMAPS_H_ */
--- /dev/null
+/*
+ * 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: storage-helper.c
+ * @desc: Helper functions to get storage details
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "storage-helper.h"
+#include "trace.h"
+#include <mntent.h>
+
+#define PATH_MAX 256
+#define INTERNAL_MEMORY_PATH "/opt/usr"
+#define EXTERNAL_MEMORY_PATH "/opt/storage/sdcard"
+
+bool is_mounted(const char* path)
+{
+ int ret = false;
+ struct mntent* mnt;
+ const char* table = "/etc/mtab";
+ FILE* fp;
+
+ fp = setmntent(table, "r");
+ if (!fp)
+ return ret;
+ while ((mnt = getmntent(fp))) {
+ if (!strcmp(mnt->mnt_dir, path)) {
+ ret = true;
+ break;
+ }
+ }
+ endmntent(fp);
+ return ret;
+}
+
+resourced_ret_c storage_get_size(int type, struct statvfs *buf)
+{
+ int ret;
+ char path[PATH_MAX] = "";
+ char errbuf[PATH_MAX];
+
+ if (type == INTERNAL)
+ snprintf(path, sizeof(path),"%s", INTERNAL_MEMORY_PATH);
+ else if (type == EXTERNAL)
+ snprintf(path, sizeof(path), "%s", EXTERNAL_MEMORY_PATH);
+ else {
+ _E("Unsupported storage type:%d", type);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ _I("Path:%s", path);
+ if (type == EXTERNAL) {
+ if (!is_mounted(EXTERNAL_MEMORY_PATH)) {
+ memset(buf, 0, sizeof(struct statvfs));
+ return RESOURCED_ERROR_NONE;
+ }
+ }
+
+ ret = statvfs(path, buf);
+ if (ret) {
+ _E("statvfs() failed. Path:%s err:%s", path, strerror_r(errno, errbuf, sizeof(errbuf)));
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 storage-helper.h
+ * @desc Helper functions to get storage details
+ */
+
+#ifndef _RESOURCED_STORAGE_HELPER_H_
+#define _RESOURCED_STORAGE_HELPER_H_
+
+#include "resourced.h"
+#include <stdbool.h>
+#include <sys/statvfs.h>
+
+enum storage_type {
+ INTERNAL = 1,
+ EXTERNAL
+};
+
+bool is_mounted(const char* path);
+
+/**
+ * @desc gets storage details
+ * @param type-INTERNAL/EXTERNAL, buf-storage details
+ * @return negative value if error
+ */
+resourced_ret_c storage_get_size(int type, struct statvfs *buf);
+
+#endif /*_RESOURCED_STORAGE_HELPER_H_*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 swap-common.h
+ * @desc swap common process
+ **/
+
+#ifndef __SWAP_COMMON_H__
+#define __SWAP_COMMON_H__
+
+#include "memory-common.h"
+
+enum swap_state {
+ SWAP_ARG_START = -1,
+ SWAP_OFF,
+ SWAP_ON,
+ SWAP_ARG_END,
+};
+
+struct swap_status_msg {
+ enum memcg_type type;
+ struct memcg_info *info;
+ pid_t pid;
+};
+
+enum swap_compact_reason {
+ SWAP_COMPACT_LOWMEM_LOW,
+ SWAP_COMPACT_LOWMEM_MEDIUM,
+ SWAP_COMPACT_SWAP_FULL,
+ SWAP_COMPACT_RESASON_MAX,
+};
+
+enum swap_state swap_get_state(void);
+
+#endif /* __SWAP_COMMON_H__ */
--- /dev/null
+/*
+ * 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: systemd-util.c
+ * @desc: systemd helper utility function
+ */
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "macro.h"
+#include "util.h"
+#include "trace.h"
+#include "edbus-handler.h"
+
+static int systemd_get_unit_obj_path(const char *unit_name,
+ char **obj_path,
+ char **err_msg)
+{
+ DBusMessage *msg = NULL;
+ DBusError err;
+ char *path = NULL;
+ char *pa[1];
+ char buf[256];
+ int r = 0;
+ char *str_err;
+
+ assert(unit_name);
+ assert(obj_path);
+ assert(err_msg && !*err_msg);
+
+ pa[0] = (char *)unit_name;
+
+ msg = dbus_method_sync("org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager", "GetUnit",
+ "s", pa);
+
+ if (!msg) {
+ _E("failed to method call(GetUnit) to systemd");
+ *err_msg = strndup("method call(GetUnit) failed", 30);
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+ dbus_error_init(&err);
+
+ if (!dbus_message_get_args(msg, &err,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ _E("failed to get object path: %s", err.message);
+ *err_msg = strndup(err.message, strlen(err.message));
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+ *obj_path = strndup(path, strlen(path));
+ if (!*obj_path) {
+ _E("failed to duplicate object path: %s", strerror_r(ENOMEM, buf, sizeof(buf)));
+ *err_msg = strndup(str_err, strlen(str_err));
+ if (!*err_msg)
+ _E("failed to duplicate dbus error message");
+
+ r = -ENOMEM;
+ }
+
+finish:
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+
+ return r;
+}
+
+int systemd_get_service_property_as_uint32(const char *unit_name,
+ const char *property,
+ unsigned int *result,
+ char **err_msg)
+{
+ _cleanup_free_ char *obj_path = NULL;
+ DBusMessageIter iter, sub;
+ DBusMessage *reply = NULL;
+ /* DBusError err; */
+ char *pa[2];
+ char buf[256];
+ char *str_err;
+ int r;
+
+ assert(unit_name);
+ assert(property);
+ assert(result);
+ assert(err_msg && !*err_msg);
+
+ r = systemd_get_unit_obj_path(unit_name, &obj_path, err_msg);
+ if (r < 0) {
+ _E("failed to get object path of %s: %s", unit_name, *err_msg);
+ goto finish;
+ }
+
+ pa[0] = "org.freedesktop.systemd1.Service";
+ pa[1] = (char *)property;
+
+ reply = dbus_method_sync("org.freedesktop.systemd1",
+ obj_path,
+ "org.freedesktop.DBus.Properties", "Get",
+ "ss", pa);
+ if (!reply) {
+ _E("failed to get property method call to systemd");
+ *err_msg = strndup("get property method call failed", 30);
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(reply, &iter)) {
+ _E("failed to init iterator");
+ *err_msg = strndup("failed to init iterator", 30);
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+ _E("arg is not type of variant");
+ str_err = strerror_r(EINVAL, buf, sizeof(buf));
+ *err_msg = strndup(str_err, strlen(str_err));
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32) {
+ _E("arg is not type of int32");
+ str_err = strerror_r(EINVAL, buf, sizeof(buf));
+ *err_msg = strndup(str_err, strlen(str_err));
+ if (!*err_msg) {
+ _E("failed to duplicate dbus error message");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ r = RESOURCED_ERROR_FAIL;
+ goto finish;
+ }
+
+ dbus_message_iter_get_basic(&sub, result);
+
+finish:
+ if (reply)
+ dbus_message_unref(reply);
+ /* dbus_error_free(&err); */
+
+ return r;
+}
--- /dev/null
+/*
+ * 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 systemd-util.h
+ * @desc Generic Helper functions
+ */
+
+#ifndef _RESOURCED_SYSTEMD_UTIL_H_
+#define _RESOURCED_SYSTEMD_UTIL_H_
+
+int systemd_get_service_property_as_uint32(const char *unit_name, const char *property, unsigned int *result, char **err_msg);
+
+#endif /*_RESOURCED_SYSTEMD_UTIL_H_*/
--- /dev/null
+/*
+ * 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: time-helper.c
+ * @desc: Helper functions for getting current timestanp and time difference
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include "time-helper.h"
+#include "trace.h"
+#include "macro.h"
+#include <mntent.h>
+
+#define TIMESTAMP_LEN 16
+#define MILLISEC 1000
+
+void time_stamp(char *timestamp)
+{
+ char ts[TIMESTAMP_LEN];
+ struct timeval curTime;
+ int milli;
+ struct tm local;
+
+ gettimeofday(&curTime, NULL);
+ milli = curTime.tv_usec / MILLISEC;
+ if (!localtime_r(&curTime.tv_sec, &local))
+ return;
+ /* Current timestamp */
+ strftime(ts,TIMESTAMP_LEN,"%y%m%d%H%M%S",&local);
+ /* Append milliseconds */
+ snprintf(timestamp, sizeof(ts) + 8, "%s%d", ts, milli);
+}
+
+void time_diff(struct timeval *diff,
+ struct timeval *start,
+ struct timeval *end)
+{
+ if (diff == NULL)
+ return;
+
+ diff->tv_sec = end->tv_sec - start->tv_sec ;
+ diff->tv_usec = end->tv_usec - start->tv_usec;
+
+ while (diff->tv_usec < 0) {
+ diff->tv_usec += 1000000;
+ diff->tv_sec -= 1;
+ }
+}
--- /dev/null
+/*
+ * 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 time-helper.h
+ * @desc Helper functions for to get timestamp and time difference
+ */
+
+#ifndef _RESOURCED_TIME_HELPER_H_
+#define _RESOURCED_TIME_HELPER_H_
+
+#include "resourced.h"
+
+/**
+ * @desc gets current timestamp in "%y%m%d%H%M%S%ms" format
+ * @param ts - timestamp string
+ * @return None
+ */
+void time_stamp(char *ts);
+
+/**
+ * @desc gets time difference between two timeval
+ * @param diff-differece, start-starting timeval, end-ending timeval
+ * @return None
+ */
+void time_diff(struct timeval *diff, struct timeval *start, struct timeval *end);
+
+#endif /*_RESOURCED_TIME_HELPER_H_*/
--- /dev/null
+/*
+ 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.
+
+ 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: trace.c
+ * @desc: log path handling functions
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "macro.h"
+#include "util.h"
+#include "trace.h"
+
+static FILE *log_file = NULL;
+static int stdout_bak = -1;
+static int stderr_bak = -1;
+API enum log_type logtype = LOG_TYPE_DLOG;
+
+int log_close(void)
+{
+ int r;
+
+ if (!log_file)
+ return 0;
+
+ if (fileno(log_file) == STDERR_FILENO)
+ return 0;
+
+ fclose(log_file);
+ log_file = NULL;
+
+ if (stdout_bak >= 0) {
+ r = dup2(stdout_bak, STDOUT_FILENO);
+ if (r < 0)
+ return -errno;
+
+ close(stdout_bak);
+ }
+
+ if (stderr_bak >= 0) {
+ r = dup2(stderr_bak, STDERR_FILENO);
+ if (r < 0)
+ return -errno;
+
+ close(stderr_bak);
+ }
+
+ return 0;
+}
+
+static int log_file_backup(FILE *f)
+{
+ int r, fd;
+
+ stdout_bak = dup(STDOUT_FILENO);
+ stderr_bak = dup(STDERR_FILENO);
+
+ fd = fileno(log_file);
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0)
+ return -errno;
+
+ r = dup2(fd, STDERR_FILENO);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int log_open_file(const char *path)
+{
+ assert(path);
+
+ if (log_file)
+ return -EALREADY;
+
+ log_file = fopen(path, "a+");
+ if (!log_file)
+ return -errno;
+
+ return log_file_backup(log_file);
+}
+
+int log_open(enum log_type type, const char *path)
+{
+ logtype = type;
+
+ switch (type) {
+ case LOG_TYPE_FILE:
+ return log_open_file(path);
+
+ default:
+ return log_close();
+ }
+}
+
+static int vlog_write(int level, const char *format, va_list ap)
+{
+ _cleanup_free_ char *buff = NULL;
+ int r;
+
+ r = vasprintf(&buff, format, ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ fprintf(log_file ? log_file : level <= LOG_ERR ? stderr : stdout,
+ "%s\n", buff);
+
+ return 0;
+}
+
+int log_write(int level, const char *format, ...)
+{
+ va_list ap;
+ int r;
+
+ va_start(ap, format);
+ r = vlog_write(level, format, ap);
+ va_end(ap);
+
+ return r;
+}
--- /dev/null
+/*
+ * 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 trace.h
+ * Common macros for tracing
+ */
+
+#ifndef _SYSTEM_RESOURCE_TRACE_H_
+#define _SYSTEM_RESOURCE_TRACE_H_
+
+#include "config.h"
+#include <dlog.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/syslog.h>
+
+#include "macro.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RESOURCED"
+
+#define WALK_TREE(list, func) g_tree_foreach((GTree *)list, func, NULL)
+
+enum log_type {
+ LOG_TYPE_STANDARD = 0,
+ LOG_TYPE_FILE,
+ LOG_TYPE_DLOG,
+ LOG_TYPE_MAX
+};
+
+extern enum log_type logtype;
+
+#define _E(fmt, arg...) do { \
+ switch (logtype) { \
+ case LOG_TYPE_DLOG: \
+ LOGE(fmt, ##arg); \
+ default: \
+ log_write(LOG_ERR, fmt, ##arg); \
+ } \
+} while (0)
+
+#define _D(fmt, arg...) do { \
+ switch (logtype) { \
+ case LOG_TYPE_DLOG: \
+ LOGD(fmt, ##arg); \
+ default: \
+ log_write(LOG_DEBUG, fmt, ##arg); \
+ } \
+} while (0)
+
+#define _I(fmt, arg...) do { \
+ switch (logtype) { \
+ case LOG_TYPE_DLOG: \
+ LOGD(fmt, ##arg); \
+ default: \
+ log_write(LOG_INFO, fmt, ##arg); \
+ } \
+} while (0)
+
+#define _SE(fmt, arg...) SECURE_LOGE(fmt, ##arg)
+#define _SD(fmt, arg...) SECURE_LOGD(fmt, ##arg)
+#define _SI(fmt, arg...) SECURE_LOGI(fmt, ##arg)
+
+#define TRACE_DB_ERR(a) if (a != NULL) { \
+ _D("%s\n", a); \
+ sqlite3_free(a); \
+}
+
+#define TRACE_RET_ERRCODE(type, error_code) do { \
+ char buf[256]; \
+ _##type("errno %d, errmsg %s", error_code, strerror_r(-error_code, buf, sizeof(buf))); \
+} while (0)
+
+#define DTRACE_RET_ERRCODE(error_code) TRACE_RET_ERRCODE(D, error_code)
+
+#define ETRACE_RET_ERRCODE(error_code) TRACE_RET_ERRCODE(E, error_code)
+
+#define TRACE_RET_ERRCODE_MSG(type, error_code, fmt, arg...) do { \
+ char buf[256]; \
+ _##type(fmt, ##arg); \
+ _##type("errno %d, errmsg %s", error_code, strerror_r(-error_code, buf, sizeof(buf))); \
+} while (0)
+
+#define DTRACE_RET_ERRCODE_MSG(error_code, fmt, arg...) \
+ TRACE_RET_ERRCODE_MSG(D, error_code, fmt, ##arg)
+
+#define ETRACE_RET_ERRCODE_MSG(error_code, fmt, arg...) \
+ TRACE_RET_ERRCODE_MSG(E, error_code, fmt, ##arg)
+
+#define DTRACE_ERRNO() TRACE_RET_ERRCODE(D, -errno)
+
+#define ETRACE_ERRNO() TRACE_RET_ERRCODE(E, -errno)
+
+#define DTRACE_ERRNO_MSG(fmt, arg...) \
+ TRACE_RET_ERRCODE_MSG(D, -errno, fmt, ##arg)
+
+#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);
+
+API int log_close(void);
+API int log_open(enum log_type type, const char *path);
+API int log_write(int level, const char *format, ...);
+
+#endif /* _SYSTEM_RESOURCE_TRACE_H_ */
--- /dev/null
+/*
+ * @file transmission.h
+ * @brief Kernel - user space transmition structures
+ *
+ */
+
+#ifndef _TRAFFIC_CONTROL_TRAFFIC_STAT_TRANSMITION_H_
+#define _TRAFFIC_CONTROL_TRAFFIC_STAT_TRANSMITION_H_
+#ifdef _KERNEL_
+#include <linux/socket.h>
+#include <linux/types.h>
+#else
+#include <netinet/in.h>
+#include <sys/types.h>
+#endif
+
+/* Used both in kernel module and in control daemon */
+
+/*
+ * @brief Entity for outgoing and incomming packet counter information.
+ * Used for serialization.
+ */
+struct traffic_event {
+ u_int32_t sk_classid;
+ unsigned long bytes;
+ int ifindex;
+};
+
+enum traffic_restriction_type {
+ RST_UNDEFINDED,
+ RST_SET,
+ RST_UNSET,
+ RST_EXCLUDE,
+ RST_MAX_VALUE
+};
+
+/*
+ * @brief Traffic restriction structure for serialization
+ * type - traffic_restriction_type
+ */
+struct traffic_restriction {
+ u_int32_t sk_classid;
+ int type;
+ int ifindex;
+ int send_limit;
+ int rcv_limit;
+ int snd_warning_threshold;
+ int rcv_warning_threshold;
+};
+
+#define RESOURCED_ALL_IFINDEX 1
+
+#endif /*TRAFFIC_CONTROL_TRAFFIC_STAT_TRANSMITION */
--- /dev/null
+/*
+ * 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: util.c
+ * @desc: Generic Helper functions
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "util.h"
+#include "trace.h"
+#include "resourced.h"
+
+int exec_cmd(char *argv[], char *filename)
+{
+ int status = 0;
+ int fd = -1;
+
+ if (fork() == 0) {
+ fd= open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+ if (fd < 0) {
+ _E("Failed to create/open file %s", filename);
+ return RESOURCED_ERROR_FAIL;
+ }
+ dup2(fd, 1);
+ status = execvp(argv[0], argv);
+ close(fd);
+ }
+ return status;
+}
+
+bool streq_ptr(const char *a, const char *b)
+{
+
+ /* Like streq(), but tries to make sense of NULL pointers */
+
+ if (a && b)
+ return streq(a, b);
+
+ if (!a && !b)
+ return true;
+
+ return false;
+}
+
+int parse_boolean(const char *v)
+{
+ assert(v);
+
+ if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' ||
+ strcaseeq(v, "on") || strcaseeq(v, "enable") || strcaseeq(v, "enabled"))
+ return 1;
+ else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' ||
+ strcaseeq(v, "off") || strcaseeq(v, "disable") || strcaseeq(v, "disabled"))
+ return 0;
+
+ return -EINVAL;
+}
+
+int parse_bytes(const char *b, size_t *s)
+{
+ _cleanup_free_ char *num = NULL;
+ size_t len, num_l, unit_l;
+
+ assert(b);
+
+ len = strlen(b);
+
+ if (!len)
+ return 0;
+
+ num_l = strspn(b, "0123456789");
+ if (num_l < len-1)
+ return -EINVAL;
+
+ unit_l = strcspn(b, "BKMG");
+ if (num_l != unit_l)
+ return -EINVAL;
+
+ num = strndup(b, num_l);
+ if (!num)
+ return -ENOMEM;
+
+ switch (b[len - 1]) {
+ case 'G':
+ *s = atoi(num) << 30;
+ break;
+ case 'M':
+ *s = atoi(num) << 20;
+ break;
+ case 'K':
+ *s = atoi(num) << 10;
+ break;
+ case 'B':
+ default:
+ *s = atoi(num);
+ break;
+ }
+
+ return 0;
+}
+
+/* Split a string into words. */
+char *split(const char *c, size_t *l, const char *separator, char **state) {
+ char *current;
+
+ current = *state ? *state : (char*) c;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, separator);
+ *l = strcspn(current, separator);
+ *state = current+*l;
+
+ return (char*) current;
+}
+
+char *truncate_nl(char *s)
+{
+ assert(s);
+
+ s[strcspn(s, NEWLINE)] = 0;
+
+ return s;
+}
+
+char *strstrip(char *s)
+{
+ char *e;
+
+ /* Drops trailing whitespace. Modifies the string in
+ * place. Returns pointer to first non-space character */
+
+ s += strspn(s, WHITESPACE);
+
+ for (e = strchr(s, 0); e > s; e --)
+ if (!strchr(WHITESPACE, e[-1]))
+ break;
+
+ *e = 0;
+
+ return s;
+}
+
+int str_to_strv(const char *str, char ***strv, const char *seperator)
+{
+ char *w, *state, *p;
+ char **v = NULL, **new = NULL;
+ size_t l;
+ size_t i = 0;
+
+ FOREACH_WORD_SEPARATOR(w, l, str, seperator, state) {
+ p = strndup(w, l);
+ if (!p) {
+ if (v)
+ free(v);
+ return -ENOMEM;
+ }
+
+ new = (char **)realloc(v, sizeof(char *) * (i + 2));
+ if (!new) {
+ free(p);
+ p = NULL;
+ return -ENOMEM;
+ }
+
+ v = new;
+
+ v[i] = p;
+ v[i+1] = NULL;
+ i++;
+ }
+
+ *strv = v;
+
+ return 0;
+}
+
+size_t sizeof_strv(const char **strv)
+{
+ size_t u = 0;
+
+ assert(strv);
+
+ while(strv[u++])
+ ;
+
+ return u - 1;
+}
+
+int strv_attach(char **first, char **second, char ***strv, bool free_second)
+{
+ char **new = NULL;
+ size_t n1 = 0, n2 = 0;
+
+ assert(strv);
+
+ if (first)
+ n1 = sizeof_strv((const char **)first);
+
+ if (second) {
+ n2 = sizeof_strv((const char **)second);
+
+ new = (char **)realloc(first, sizeof(char *) * (n1 + n2 + 1));
+ if (!new)
+ return -ENOMEM;
+
+ first = new;
+
+ memcpy(first + n1, second, sizeof(char *) * (n2 + 1));
+
+ if (free_second)
+ free(second);
+ }
+
+ *strv = first;
+
+ return 0;
+}
+
+void strv_free_full(char **strv)
+{
+ char **s;
+
+ if (!strv)
+ return;
+
+ FOREACH_STRV(s, strv) {
+ if (s && *s) {
+ free(*s);
+ *s = NULL;
+ }
+ }
+
+ free(strv);
+ strv = NULL;
+}
--- /dev/null
+/*
+ * 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 util.h
+ * @desc Generic Helper functions
+ */
+
+#ifndef _RESOURCED_UTIL_H_
+#define _RESOURCED_UTIL_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+
+#define COMMENT '#'
+#define COMMENTS "#;"
+#define NEWLINE "\n\r"
+#define WHITESPACE " \t\n\r"
+
+#define _pure_ __attribute__ ((pure))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+static inline void freep(void *p)
+{
+ free(*(void**) p);
+}
+
+static inline void closep(int *fd)
+{
+ if (*fd >= 0)
+ close(*fd);
+}
+
+static inline void fclosep(FILE **f)
+{
+ if (*f)
+ fclose(*f);
+}
+
+static inline void pclosep(FILE **f)
+{
+ if (*f)
+ pclose(*f);
+}
+
+static inline void closedirp(DIR **d)
+{
+ if (*d)
+ closedir(*d);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+
+#define NUM_DIFF(x, y) ((x > y) ? (x - y) : (y -x))
+
+#define BYTE_TO_KBYTE(b) ((b) >> 10)
+#define BYTE_TO_MBYTE(b) ((b) >> 20)
+#define BYTE_TO_GBYTE(b) ((b) >> 30)
+
+#define KBYTE_TO_BYTE(k) ((k) << 10)
+#define KBYTE_TO_MBYTE(k) ((k) >> 10)
+#define KBYTE_TO_GBYTE(k) ((k) >> 20)
+
+#define GBYTE_TO_BYTE(g) ((g) << 30)
+#define GBYTE_TO_KBYTE(g) ((g) << 20)
+#define GBYTE_TO_MBYTE(g) ((g) << 10)
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+#define new(t, n) ((t*) malloc(sizeof(t) * (n)))
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+#define malloc0(n) (calloc((n), 1))
+
+static inline bool is_empty(const char *p)
+{
+ return !p || !p[0];
+}
+
+static inline bool strstart_with(const char *str, const char *with)
+{
+ return strncmp(str, with, strlen(with)) == 0;
+}
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); \
+ (word); \
+ (word) = split((s), &(length), (separator), &(state)))
+
+#define FOREACH_WORD(word, length, s, state) \
+ FOREACH_WORD_SEPARATOR(word, length, s, WHITESPACE, state)
+
+#define FOREACH_DIRENT(de, d, result, on_error) \
+ for (errno = readdir_r(d, &de, &result);; errno = readdir_r(d, &de, &result)) \
+ if (errno || !result) { \
+ if (errno) \
+ on_error; \
+ break; \
+ } else if (streq(de.d_name, ".") || \
+ streq(de.d_name, "..")) \
+ continue; \
+ else
+
+#define FOREACH_STRV(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
+/**
+ * @desc executes given command and dumps output to a file
+ * @param argv - command to be executed with parameters
+ filename - output file
+ * @return None
+ */
+int exec_cmd(char *argv[], char *filename);
+
+bool streq_ptr(const char *a, const char *b) _pure_;
+int parse_boolean(const char *v) _pure_;
+int parse_bytes(const char *b, size_t *s) _pure_;
+char *split(const char *c, size_t *l, const char *separator, char **state);
+char *truncate_nl(char *s);
+char *strstrip(char *s);
+int str_to_strv(const char *str, char ***strv, const char *seperator);
+size_t sizeof_strv(const char **strv);
+int strv_attach(char **first, char **second, char ***strv, bool free_second);
+void strv_free_full(char **strv);
+#endif /*_RESOURCED_UTIL_H_*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 cpu.c
+ *
+ * @desc cpu module
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+#include <dirent.h>
+#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 "procfs.h"
+#include "proc-common.h"
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "resourced.h"
+#include "trace.h"
+#include "vconf.h"
+#include "cgroup.h"
+#include "config-parser.h"
+#include "const.h"
+#include "file-helper.h"
+
+/*
+ * CPU cgroup had four kinds of directories.
+ * 1. /sys/fs/cgroup/cpu : default group, all system daemons
+ * 2. /sys/fs/cgroup/cpu/background : control cpu.shares value
+ * preloaded service application, downloadable widget, system services
+ * 3. /sys/fs/cgroup/cpu/download : control cpu.shares and maximum quota
+ * downloadable service application
+ * 4. /sys/fs/cgroup/cpu/background/quota : control cpu.shares and minimum quota
+ * all (preloaded and downloadable) background UI application
+ */
+
+#define CPU_DEFAULT_CGROUP "/sys/fs/cgroup/cpu"
+#define CPU_CONTROL_GROUP "/sys/fs/cgroup/cpu/background"
+#define CPU_CONTROL_DOWNLOAD_GROUP "/sys/fs/cgroup/cpu/background/download"
+#define CPU_CONTROL_CPUQUOTA_GROUP "/sys/fs/cgroup/cpu/background/quota"
+#define CPU_CONTROL_BANDWIDTH "/cpu.cfs_quota_us"
+#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_SYSTEM "SYSTEM_PREDEFINE"
+#define CPU_CONF_HOME "HOME_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 -5
+#define CPU_QUOTA_PERIOD_USEC 1000
+
+static Ecore_Timer *cpu_predefined_timer = NULL;
+static bool bCPUQuota;
+
+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)
+{
+ return cgroup_write_node(path, CGROUP_FILE_NAME, pid);
+}
+
+static bool cpu_quota_enabled(void)
+{
+ return bCPUQuota;
+}
+
+static void cpu_check_cpuquota(void)
+{
+ int ret, node = 0;
+ char buf[MAX_PATH_LENGTH];
+
+ snprintf(buf, sizeof(buf), "%s%s", CPU_DEFAULT_CGROUP, CPU_CONTROL_BANDWIDTH);
+ ret = fread_int(buf, &node);
+ if (!ret)
+ bCPUQuota = true;
+}
+
+static int load_cpu_config(struct parse_result *result, void *user_data)
+{
+ pid_t pid = 0, value;
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, CPU_CONF_SECTION))
+ return RESOURCED_ERROR_NO_DATA;
+ if (!strcmp(result->name, CPU_CONF_PREDEFINE)) {
+ 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_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_BACKGROUND_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, CPU_CONF_SYSTEM)) {
+ pid = find_pid_from_cmdline(result->value);
+ if (pid > 0)
+ cpu_move_cgroup(pid, CPU_CONTROL_GROUP);
+ } else if (!strcmp(result->name, CPU_CONF_HOME)) {
+ pid = find_pid_from_cmdline(result->value);
+ if (pid > 0) {
+ setpriority(PRIO_PROCESS, pid, CPU_HIGHAPP_PRI);
+ def_list.control[def_list.num].pid = pid;
+ def_list.control[def_list.num++].type = SET_BOOTING;
+ }
+ } else if (!strcmp(result->name, "BACKGROUND_CPU_SHARE")) {
+ value = atoi(result->value);
+ if (value) {
+ cgroup_write_node(CPU_CONTROL_GROUP, CPU_SHARE, value);
+ cgroup_write_node(CPU_CONTROL_DOWNLOAD_GROUP, CPU_SHARE, value);
+ }
+ if (cpu_quota_enabled())
+ cgroup_write_node(CPU_CONTROL_CPUQUOTA_GROUP, CPU_SHARE, value);
+ } else if (!strcmp(result->name, "BACKGROUND_CPU_MAX_QUOTA")) {
+ value = atoi(result->value);
+ if (value && cpu_quota_enabled()) {
+ value *= CPU_QUOTA_PERIOD_USEC;
+ cgroup_write_node(CPU_CONTROL_DOWNLOAD_GROUP,
+ CPU_CONTROL_BANDWIDTH, value);
+ }
+ } else if (!strcmp(result->name, "BACKGROUND_CPU_MIN_QUOTA")) {
+ value = atoi(result->value);
+ if (value && cpu_quota_enabled()) {
+ value *= CPU_QUOTA_PERIOD_USEC;
+ cgroup_write_node(CPU_CONTROL_CPUQUOTA_GROUP,
+ CPU_CONTROL_BANDWIDTH, value);
+ }
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_service_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ struct proc_app_info *pai = ps->pai;
+
+ _D("cpu_service_launch : pid = %d, appname = %s", ps->pid, ps->appid);
+ if (pai && CHECK_BIT(pai->categories, PROC_BG_SYSTEM))
+ return RESOURCED_ERROR_NONE;
+ else if (pai && CHECK_BIT(pai->flags, PROC_DOWNLOADAPP))
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_DOWNLOAD_GROUP);
+ else
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_GROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_widget_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ struct proc_app_info *pai = ps->pai;
+
+ _D("cpu_widget_background : pid = %d, appname = %s", ps->pid, ps->appid);
+ if (pai && CHECK_BIT(pai->flags, PROC_DOWNLOADAPP))
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_GROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_foreground_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ int pri;
+ _D("cpu_foreground_state : pid = %d, appname = %s", ps->pid, ps->appid);
+ pri = getpriority(PRIO_PROCESS, ps->pid);
+ if (pri == -1 || pri > CPU_DEFAULT_PRI)
+ setpriority(PRIO_PGRP, ps->pid, CPU_DEFAULT_PRI);
+ if (check_predefined(ps->pid) != SET_DEFAUT)
+ cpu_move_cgroup(ps->pid, CPU_DEFAULT_CGROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_background_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ _D("cpu_background_state : pid = %d, appname = %s", ps->pid, ps->appid);
+ setpriority(PRIO_PGRP, ps->pid, CPU_BACKGROUND_PRI);
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_GROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_restrict_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ _D("cpu_restrict_state : pid = %d, appname = %s", ps->pid, ps->appid);
+ if (CHECK_BIT(ps->pai->categories, PROC_BG_MEDIA))
+ return RESOURCED_ERROR_NONE;
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_CPUQUOTA_GROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_active_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ int oom_score_adj = 0, ret;
+ _D("cpu_active_state : pid = %d, appname = %s", ps->pid, ps->appid);
+ ret = proc_get_oom_score_adj(ps->pid, &oom_score_adj);
+ if (ret || oom_score_adj < OOMADJ_PREVIOUS_DEFAULT)
+ return RESOURCED_ERROR_NONE;
+ cpu_move_cgroup(ps->pid, CPU_DEFAULT_CGROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_prelaunch_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ struct proc_app_info *pai = ps->pai;
+ int i = 0;
+
+ if (!cpu_predefined_timer)
+ return RESOURCED_ERROR_NONE;
+ if (pai->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;
+ }
+ }
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_system_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+
+ _D("cpu receive system service : pid = %d", ps->pid);
+ cpu_move_cgroup(ps->pid, CPU_CONTROL_GROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_terminatestart_state(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ cpu_move_cgroup(ps->pid, CPU_DEFAULT_CGROUP);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_exclude_state(void *data)
+{
+ struct proc_exclude *pe = (struct proc_exclude *)data;
+ if (check_predefined(pe->pid) != SET_DEFAUT)
+ return RESOURCED_ERROR_NONE;
+ if (pe->type == PROC_INCLUDE)
+ cpu_move_cgroup(pe->pid, CPU_CONTROL_GROUP);
+ else
+ cpu_move_cgroup(pe->pid, CPU_DEFAULT_CGROUP);
+ 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;
+
+ _D("resourced_cpu_init");
+ ret_code = make_cgroup_subdir(CPU_DEFAULT_CGROUP, "background", NULL);
+ ret_value_msg_if(ret_code < 0, ret_code, "cpu init failed\n");
+ ret_code = make_cgroup_subdir(CPU_CONTROL_GROUP, "download", NULL);
+ ret_value_msg_if(ret_code < 0, ret_code, "cpu init failed\n");
+ cpu_check_cpuquota();
+ if (cpu_quota_enabled()) {
+ ret_code = make_cgroup_subdir(CPU_CONTROL_GROUP, "quota", NULL);
+ 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_state);
+ 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);
+ register_notifier(RESOURCED_NOTIFIER_SYSTEM_SERVICE, cpu_system_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_TERMINATE_START, cpu_terminatestart_state);
+ register_notifier(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, cpu_exclude_state);
+ register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_foreground_state);
+ register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_widget_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_ACTIVE, cpu_active_state);
+ if (cpu_quota_enabled())
+ register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND_READY,
+ cpu_restrict_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_cpu_finalize(void *data)
+{
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_service_state);
+ 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);
+ unregister_notifier(RESOURCED_NOTIFIER_SYSTEM_SERVICE, cpu_system_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATE_START, cpu_terminatestart_state);
+ unregister_notifier(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, cpu_exclude_state);
+ unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, cpu_foreground_state);
+ unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, cpu_widget_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_ACTIVE, cpu_active_state);
+ if (cpu_quota_enabled())
+ unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND_READY,
+ cpu_restrict_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops cpu_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "cpu",
+ .init = resourced_cpu_init,
+ .exit = resourced_cpu_finalize,
+};
+
+MODULE_REGISTER(&cpu_modules_ops)
--- /dev/null
+[CONTROL]
+# predefined process list
+LAZY_PREDEFINE=net-config
+PREDEFINE=indicator
+PREDEFINE=windicator
+BOOTING_PREDEFINE=quickpanel
+WRT_PREDEFINE=wrt_launchpad_daemon
+BOOTING_PREDEFINE=volume
+BOOTING_PREDEFINE=nvitemd
+BACKGROUND_CPU_SHARE=50
+BACKGROUND_CPU_MAX_QUOTA=50
+BACKGROUND_CPU_MIN_QUOTA=10
--- /dev/null
+/*
+ * 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 freezer-common.c
+ *
+ * @desc Freezer common library for resourced
+ */
+
+#define FREEZER_LATE_CONTROL_DISABLED 0
+
+#include "trace.h"
+
+int resourced_freezer_proc_late_control(void)
+{
+ _E("Using dummy definition of freezer_proc_get_late_control");
+ return FREEZER_LATE_CONTROL_DISABLED;
+}
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 freezer.c
+ *
+ * @desc Freezer module
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <dlfcn.h>
+
+#include "macro.h"
+#include "util.h"
+#include "module.h"
+#include "module-data.h"
+#include "notifier.h"
+#include "edbus-handler.h"
+#include "resourced.h"
+#include "trace.h"
+#include "vconf.h"
+#include "config-parser.h"
+#include "procfs.h"
+#include "proc-common.h"
+
+#include "freezer.h"
+
+#define FREEZER_MODULE_PATH "/usr/lib/libsystem-freezer.so"
+
+static int freezer_init_check;
+
+/******************************************** Freezer symbols *************************************************/
+/* Freezer cgroup late control setting retrieval */
+int (*resourced_freezer_get_proc_late_control)(void) = NULL;
+
+/* Freezer suspend status retrieval */
+static bool (*resourced_freezer_is_suspended)(void) = NULL;
+
+/* Freezer cgroup operation state */
+static enum freezer_state (*resourced_freezer_get_operation_state)(void) = NULL;
+
+/* Freezer module initialization and finalization functions */
+static int (*resourced_freezer_initialize)(void *data) = NULL;
+static int (*resourced_freezer_deinitialize)(void) = NULL;
+
+/* Freezer module dbus method calls and signal handlers */
+static DBusMessage *(*resourced_freezer_dbus_method_handler)(E_DBus_Object *obj, DBusMessage *msg) = NULL;
+static void (*resourced_freezer_dbus_signal_handler)(void *data, DBusMessage *msg) = NULL;
+
+/* Resourced notifier handlers */
+static int (*resourced_freezer_change_state_cb)(void *data) = NULL;
+static int (*resourced_freezer_service_launch)(void *data) = NULL;
+static int (*resourced_freezer_wakeup)(void *data) = NULL;
+static int (*resourced_freezer_service_wakeup)(void *data) = NULL;
+static int (*resourced_freezer_app_suspend)(void *data) = NULL;
+static int (*resourced_freezer_power_off)(void *data) = NULL;
+
+/* dlopen handle for libfreezer */
+static void *dlopen_handle;
+/****************************************** Freezer symbols end ***********************************************/
+
+/* freezer_proc_get_late_control function defined for freezer module on case */
+int resourced_freezer_proc_late_control(void)
+{
+ if (resourced_freezer_get_proc_late_control)
+ return resourced_freezer_get_proc_late_control();
+
+ _E("freezer_get_proc_late_control is not loaded!");
+ return 0;
+}
+
+/****************************************** Internal symbols **************************************************/
+static DBusMessage *resourced_freezer_dbus_method_handler_generic(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ int ret;
+
+ if (resourced_freezer_dbus_method_handler)
+ return resourced_freezer_dbus_method_handler(obj, msg);
+
+ ret = 0;
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { METHOD_GET_FREEZER_STATE, NULL, "i", resourced_freezer_dbus_method_handler_generic },
+ { METHOD_GET_FREEZER_SERVICE, NULL, "i", resourced_freezer_dbus_method_handler_generic },
+ /* Add methods here */
+};
+
+static struct edbus_method edbus_suspend_methods[] = {
+ { METHOD_SET_FREEZER_SUSPEND, "s", "i", resourced_freezer_dbus_method_handler_generic },
+ /* Add methods here */
+};
+
+static void freezer_dbus_init(bool is_suspend)
+{
+ resourced_ret_c ret;
+
+ register_edbus_signal_handler(RESOURCED_PATH_FREEZER,
+ RESOURCED_INTERFACE_FREEZER,
+ SIGNAL_FREEZER_STATUS,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+ register_edbus_signal_handler(RESOURCED_PATH_FREEZER,
+ RESOURCED_INTERFACE_FREEZER,
+ SIGNAL_FREEZER_SERVICE,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_FREEZER,
+ edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ ret_msg_if(ret != RESOURCED_ERROR_NONE,
+ "DBus method registration for %s is failed",
+ RESOURCED_PATH_FREEZER);
+
+ if (!is_suspend)
+ return;
+
+ register_edbus_signal_handler(DEVICED_PATH_DISPLAY,
+ DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDONCOMPLETE,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_FREEZER,
+ edbus_suspend_methods,
+ ARRAY_SIZE(edbus_suspend_methods));
+ ret_msg_if(ret != RESOURCED_ERROR_NONE,
+ "DBus method registration for %s is failed",
+ RESOURCED_PATH_FREEZER);
+}
+
+static bool freezer_is_present(void)
+{
+ struct stat buf;
+
+ /* Check if libfreezer.so is present or not */
+ if (stat(FREEZER_MODULE_PATH, &buf)) {
+ _E("Freezer library is not present @ %s", FREEZER_MODULE_PATH);
+ return false;
+ }
+
+ return true;
+}
+
+static void freezer_unload_symbols(void)
+{
+#define FREEZER_UNLOAD_SYMBOL(sym) \
+ sym = NULL;
+
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_get_proc_late_control);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_is_suspended);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_get_operation_state);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_initialize);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_deinitialize);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_dbus_method_handler);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_dbus_signal_handler);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_change_state_cb);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_service_launch);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_wakeup);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_service_wakeup);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_app_suspend);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_power_off);
+
+#undef FREEZER_UNLOAD_SYMBOL
+
+ if (dlopen_handle) {
+ dlclose(dlopen_handle);
+ dlopen_handle = NULL;
+ }
+}
+
+static int freezer_load_symbols(void)
+{
+ dlopen_handle = dlopen(FREEZER_MODULE_PATH, RTLD_NOW);
+ if (!dlopen_handle) {
+ _E("freezer dlopen failed!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+#define FREEZER_LOAD_SYMBOL(sym, name) \
+ sym = dlsym(dlopen_handle, name); \
+ if (!sym) { \
+ _E("failed to dlsym %s", name); \
+ goto error; \
+ } \
+
+ FREEZER_LOAD_SYMBOL(resourced_freezer_get_proc_late_control, "freezer_get_proc_late_control");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_is_suspended, "freezer_is_suspended");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_get_operation_state, "freezer_get_operation_state");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_initialize, "freezer_initialize");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_deinitialize, "freezer_finalize");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_dbus_method_handler, "freezer_dbus_method_handler");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_dbus_signal_handler, "freezer_dbus_signal_handler");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_change_state_cb, "freezer_change_state_cb");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_service_launch, "freezer_service_launch");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_wakeup, "freezer_wakeup");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_service_wakeup, "freezer_service_wakeup");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_app_suspend, "freezer_app_suspend");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_power_off, "freezer_power_off");
+
+#undef FREEZER_LOAD_SYMBOL
+
+ return RESOURCED_ERROR_NONE;
+error:
+ freezer_unload_symbols();
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int resourced_freezer_init(void *data)
+{
+ int ret_code;
+ bool is_suspend;
+ bool is_present;
+ struct freezer_init_data init_data = { .resourced_app_list = &proc_app_list };
+
+ is_present = freezer_is_present();
+ if (!is_present) {
+ _E("Freezer library not present. Not enabling freezer");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret_code = freezer_load_symbols();
+ if (ret_code != RESOURCED_ERROR_NONE) {
+ _E("Not able to load symbols. Will use default definitions");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret_code = resourced_freezer_initialize(&init_data);
+ ret_value_msg_if(ret_code < 0, ret_code, "failed to initialize freezer module\n");
+
+ is_suspend = resourced_freezer_is_suspended();
+ freezer_dbus_init(is_suspend);
+
+ if (is_suspend)
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+ resourced_freezer_service_launch);
+ register_notifier(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ resourced_freezer_change_state_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_WAKEUP,
+ resourced_freezer_wakeup);
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_WAKEUP,
+ resourced_freezer_service_wakeup);
+ register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND,
+ resourced_freezer_app_suspend);
+ register_notifier(RESOURCED_NOTIFIER_POWER_OFF,
+ resourced_freezer_power_off);
+
+ freezer_init_check = 1;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_freezer_finalize(void *data)
+{
+ if (freezer_init_check == 0)
+ return RESOURCED_ERROR_NONE;
+
+ freezer_init_check = 0;
+
+ resourced_freezer_deinitialize();
+
+ unregister_notifier(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ resourced_freezer_change_state_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+ resourced_freezer_service_launch);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_WAKEUP,
+ resourced_freezer_wakeup);
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_WAKEUP,
+ resourced_freezer_service_wakeup);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND,
+ resourced_freezer_app_suspend);
+ unregister_notifier(RESOURCED_NOTIFIER_POWER_OFF,
+ resourced_freezer_power_off);
+
+ freezer_unload_symbols();
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_freezer_status(void *data)
+{
+ struct freezer_status_data *f_data;
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (!freezer_init_check)
+ return RESOURCED_ERROR_NONE;
+
+ f_data = (struct freezer_status_data *)data;
+ switch (f_data->type) {
+ case GET_STATUS:
+ ret = resourced_freezer_get_operation_state();
+ break;
+ default:
+ _E("Unsupported command: %d; status", f_data->type);
+ }
+
+ return ret;
+}
+
+static const struct module_ops freezer_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "freezer",
+ .init = resourced_freezer_init,
+ .exit = resourced_freezer_finalize,
+ .status = resourced_freezer_status,
+};
+
+MODULE_REGISTER(&freezer_modules_ops)
--- /dev/null
+[SETTING]
+# freezer=default, psmode, suspend
+freezer=suspend
+# start freezing Nth background application
+freezebackground=1
+# service = enable, disable
+freezeservice=disable
+# legacy application under tizen 2.4 = freeze, allow
+# true: allow
+# false: freeze
+freezelegacyapp=false
+
+[PREDEFINE]
+VITAL_WAKEUP=scim
+VITAL_WAKEUP=windicator
+VITAL_WAKEUP=indicator
+VITAL_DEBUG_WAKEUP=dlog_logger
+VITAL_DISPLAY_WAKEUP=auditd
+VITAL_DISPLAY_WAKEUP=data-provider-slave
--- /dev/null
+/*
+ * 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 freezer.h
+ * @desc Freezer library definitions
+ **/
+
+
+#ifndef __FREEZER_H__
+#define __FREEZER_H__
+
+#include <glib.h>
+
+/* Freezer dbus signal names */
+#define SIGNAL_FREEZER_STATE "FreezerState"
+#define SIGNAL_FREEZER_SERVICE "FreezerService"
+#define SIGNAL_FREEZER_STATUS "FreezerStatus"
+
+/* Freezer dbus method names */
+#define METHOD_GET_FREEZER_STATE "GetFreezerState"
+#define METHOD_GET_FREEZER_SERVICE "GetFreezerService"
+#define METHOD_SET_FREEZER_SUSPEND "SetSuspend"
+
+/* Freezer cgroup state */
+enum freezer_state {
+ CGROUP_FREEZER_DISABLED,
+ CGROUP_FREEZER_ENABLED,
+ CGROUP_FREEZER_INITIALIZED,
+ CGROUP_FREEZER_PAUSED,
+ CGROUP_FREEZER_VITAL_SLEEP,
+ CGROUP_FREEZER_VITAL_WAKEUP,
+ CGROUP_FREEZER_VITAL_DISPLAY_WAKEUP,
+ CGROUP_FREEZER_VITAL_EXIT,
+};
+
+/* Freezer cgroup state request type */
+enum freezer_status_type {
+ GET_STATUS,
+ SET_STATUS,
+};
+
+/* Freezer cgroup state request payload */
+struct freezer_status_data {
+ int type;
+ int status;
+};
+
+/* Freezer module init data (to be passed from resourced) */
+struct freezer_init_data {
+ GSList **resourced_app_list;
+};
+
+/* Freezer cgroup late control setting retrieval */
+int resourced_freezer_proc_late_control(void);
+
+#endif /* __FREEZER_H__ */
+
--- /dev/null
+/*
+ * 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 decision-memory.c
+ *
+ * @desc start memory decision system for resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <glib.h>
+
+#include "resourced.h"
+#include "const.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "notifier.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "decision.h"
+#include "decision-memory.h"
+
+#define DECISION_MEMORY_THRES_HIT 20
+#define DECISION_MEMORY_THRES_LEAK 200
+
+enum decision_memory_args {
+ DECISION_MEMORY_PSS,
+ DECISION_MEMORY_USS,
+};
+
+static int decision_is_leak_warning(struct regression_info *ri)
+{
+ if (ri->hit > DECISION_MEMORY_THRES_HIT &&
+ ri->coeff_a > DECISION_MEMORY_THRES_LEAK)
+ return RESOURCED_ERROR_NONE;
+
+ return RESOURCED_ERROR_FAIL;
+}
+
+static void decision_regression_info_update(struct decision_memory_info *dmi, unsigned int uss)
+{
+ struct regression_info *ri;
+ unsigned N, x, y, error;
+ long d, an, bn;
+
+ if (!dmi || !dmi->ri) {
+ _E("decision memory info or regression info is null");
+ return;
+ }
+
+ ri = dmi->ri;
+
+ if (!ri) {
+ _D("regression info is null");
+ return;
+ }
+
+ if (dmi->pred_uss) {
+ int hit = 0;
+ error = (float)dmi->pred_uss * 0.1;
+ if (dmi->pred_uss - error <= uss &&
+ dmi->pred_uss + error >= uss) {
+ ri->hit++;
+ hit = 1;
+ }
+ _D("uss = %u, pred_uss = %u, hit = %u, error = %u, hit = %d\n",
+ uss, dmi->pred_uss, ri->hit, error, hit);
+ if (hit) {
+ if (decision_is_leak_warning(ri) >= 0) {
+ dmi->warning_leak = 1;
+ _D("hit = %d, coeff_a = %.2f, douted leak",
+ ri->hit, ri->coeff_a);
+ }
+ } else {
+ ri->hit = 0;
+ dmi->warning_leak = 0;
+ }
+ }
+
+ /* On first sample, sample_count and x value starts from 1 */
+ x = ++ri->sample_count;
+ y = uss;
+ N = x;
+ ri->sum_x += x;
+ ri->sum_y += y;
+ ri->sum_xs += x * x;
+ ri->sum_xy += x * y;
+ d = ((long)N * ri->sum_xs) - ((long)ri->sum_x * ri->sum_x);
+
+ if (!d) {
+ _D("denominator for coefficient a and b is zero");
+ return;
+ }
+
+ an = ((long)N * ri->sum_xy) - ((long)ri->sum_x * ri->sum_y);
+ bn = ((long)ri->sum_y * ri->sum_xs) - ((long)ri->sum_x * ri->sum_xy);
+ ri->coeff_a = (float)an / d;
+ ri->coeff_b = (float)bn / d;
+
+ /*
+ * pred_uss is the prediction of next expected uss.
+ * and it can be calculated using next x value, (x + 1).
+ * When regression eq. is y = a * x + b,
+ * predicted y can be calculated using a * (x + 1) + b.
+ */
+ dmi->pred_uss = (ri->coeff_a * (x + 1)) + ri->coeff_b;
+ _D("x = %u, y = %u, a = %.2f, b = %.2f, pred_uss = %u, an = %ld, bn = %ld, d = %ld",
+ x, y, ri->coeff_a, ri->coeff_b, dmi->pred_uss, an, bn, d);
+
+}
+
+static struct regression_info *new_regression_info(void)
+{
+ struct regression_info *ri;
+
+ ri = malloc(sizeof(struct regression_info));
+ if (!ri) {
+ _D("fail to allocate regression_info");
+ return NULL;
+ }
+
+ memset(ri, 0, sizeof(struct regression_info));
+
+ return ri;
+}
+
+static void *decision_memory_info_create(void)
+{
+ struct decision_memory_info *dmi;
+
+ dmi = malloc(sizeof(struct decision_memory_info));
+ if (!dmi) {
+ _D("fail to allocate decision_memory_info");
+ return NULL;
+ }
+
+ dmi->pred_uss = 0;
+ dmi->warning_leak = 0;
+
+ dmi->ri = new_regression_info();
+ if (!dmi->ri) {
+ _E("fail to allocate regression info");
+ free(dmi);
+ return NULL;
+ }
+
+ return (void *)dmi;
+}
+
+static void decision_memory_info_free(void *data)
+{
+ struct decision_memory_info *dmi;
+
+ if (!data)
+ return;
+ dmi = (struct decision_memory_info *)data;
+ if (dmi->ri)
+ free(dmi->ri);
+ free(dmi);
+}
+
+static void decision_memory_info_update(struct decision_item *di, void *info)
+{
+ struct decision_memory_info *dmi = (struct decision_memory_info *)info;
+
+ _D("decision update regression info for %s", di->ai->appid);
+ decision_regression_info_update(dmi, di->args[DECISION_MEMORY_USS]);
+}
+
+static void decision_memory_info_write(void *data, char *buf, int len)
+{
+ struct decision_memory_info *dmi;
+
+ if (!data) {
+ snprintf(buf, len, "invalid");
+ return;
+ }
+ dmi = (struct decision_memory_info *)data;
+ snprintf(buf, len, "%s", dmi->warning_leak ? "Y" : "N");
+}
+
+static void decision_memory_updated_cb(char *data)
+{
+ char appid[MAX_APPID_LENGTH];
+ char pkgname[MAX_PKGNAME_LENGTH];
+ struct decision_item *di;
+ unsigned pss, uss;
+ int time, len;
+
+ if (!data)
+ return;
+
+ len = sscanf(data, "%s %s %d %u %u", appid, pkgname, &time, &pss, &uss);
+ if (len < 0) {
+ _E("sscanf failed");
+ return;
+ }
+ _D("%s: %s %s %d %u %u", data, appid, pkgname, time, pss, uss);
+
+ di = decision_item_new(DECISION_MEMORY, appid, pkgname);
+
+ if (!di)
+ return;
+
+ di->args[DECISION_MEMORY_PSS] = pss;
+ di->args[DECISION_MEMORY_USS] = uss;
+
+ decision_queue_item_insert(di);
+ decision_update_start();
+}
+
+static const struct decision_module decision_memory = {
+ .type = DECISION_MEMORY,
+ .create = decision_memory_info_create,
+ .free = decision_memory_info_free,
+ .update = decision_memory_info_update,
+ .write = decision_memory_info_write,
+};
+
+int decision_memory_init(void *data)
+{
+ _D("decision memory init finished");
+
+ logging_register_listener("memory", decision_memory_updated_cb);
+ decision_module_register(&decision_memory);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int decision_memory_exit(void *data)
+{
+ _D("decision memory finalize");
+
+ decision_module_unregister(&decision_memory);
+ logging_unregister_listener(DECISION_MEMORY,
+ decision_memory_updated_cb);
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 decision.c
+ *
+ * @desc decision for resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <Ecore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <glib.h>
+#include <sys/resource.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "notifier.h"
+#include "proc-common.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "edbus-handler.h"
+#include "decision.h"
+#include "filemap.h"
+
+#define DECISION_FILE_PATH HEART_FILE_PATH"/.decision.dat"
+#define DECISION_FILE_SIZE (512 * 1024)
+/* To Do: make configurable */
+#define DECISION_WRITE_INTERVAL 60 * 30 /* 30 minute */
+#define DECISION_PRIORITY 20
+
+enum {
+ DECISION_THREAD_UPDATE,
+ DECISION_THREAD_WRITE,
+ DECISION_THREAD_MAX,
+};
+
+static pthread_mutex_t decision_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static GHashTable *decision_app_list;
+
+static pthread_mutex_t decision_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+static GQueue *decision_queue;
+static struct filemap *fm;
+
+static const struct decision_module *decision_modules[DECISION_MAX];
+static int num_decisions;
+
+struct decision_thread {
+ pthread_t thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+static struct decision_thread decisions[DECISION_THREAD_MAX];
+
+static Ecore_Timer *decision_write_timer = NULL;
+
+static const struct decision_module *decision_module_find(int type)
+{
+ if (type < 0 || type >= DECISION_MAX)
+ return NULL;
+
+ return decision_modules[type];
+}
+
+static void* decision_info_find(struct decision_table *dt, int type)
+{
+ if (!dt || type < 0 || type >= DECISION_MAX)
+ return NULL;
+
+ return dt->infos[type];
+}
+
+static int decision_table_init(struct decision_table *dt, const char *appid,
+ const char *pkgname)
+{
+ int i;
+
+ if (!dt) {
+ _E("parameter dt is null");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dt->ai = resourced_appinfo_get(dt->ai, appid, pkgname);
+
+ assert(dt->ai);
+
+ dt->updated = 0;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm;
+ void *info;
+
+ dm = decision_module_find(i);
+ if (!dm) {
+ _E("cannot find decision module");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ info = dm->create();
+
+ if (!info) {
+ _E("cannot create info for type %d", i);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dt->infos[i] = info;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct decision_table *decision_table_new(const char *appid,
+ const char *pkgname)
+{
+ struct decision_table *dt;
+ int ret;
+
+ dt = calloc(1, sizeof(struct decision_table));
+ if (!dt) {
+ _E("fail to allocate decision_table");
+ return NULL;
+ }
+
+ ret = decision_table_init(dt, appid, pkgname);
+ if (ret < 0) {
+ _E("fail to init decision table for appid = %s", appid);
+ free(dt);
+ return NULL;
+ }
+
+ g_hash_table_insert(decision_app_list, (gpointer)dt->ai->appid,
+ (gpointer)dt);
+
+ return dt;
+}
+
+static void decision_table_free(gpointer value)
+{
+ struct decision_table *dt = (struct decision_table *)value;
+ int i;
+
+ if (!dt)
+ return;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm;
+ void * info;
+
+ dm = decision_module_find(i);
+ info = decision_info_find(dt, i);
+
+ if (dm && info)
+ dm->free(info);
+ }
+ resourced_appinfo_put(dt->ai);
+ free(dt);
+}
+
+static struct decision_table *decision_table_find(const char *appid,
+ const char *pkgname)
+{
+ struct decision_table *dt;
+
+ if (!decision_app_list) {
+ _E("decision app list was not created");
+ return NULL;
+ }
+
+ dt = g_hash_table_lookup(decision_app_list, (gconstpointer)appid);
+ if (!dt) {
+ dt = decision_table_new(appid, pkgname);
+ if (!dt) {
+ _E("cannot create decision table for %s", appid);
+ return NULL;
+ }
+ }
+
+ return dt;
+}
+
+struct decision_item *decision_item_new(int type, const char *appid, const char *pkgname)
+{
+ struct decision_item *di;
+
+ di = malloc(sizeof(struct decision_item));
+ if (!di) {
+ _E("fail to allocate decision_item");
+ return NULL;
+ }
+
+ memset(di, 0, sizeof(struct decision_item));
+ di->type = type;
+ di->ai = resourced_appinfo_get(di->ai, appid, pkgname);
+
+ if (!di->ai) {
+ _E("fail to get appinfo for appid = %s, pkgname = %s",
+ appid, pkgname);
+ free(di);
+ return NULL;
+ }
+
+ return di;
+}
+
+static void decision_item_free(struct decision_item *di)
+{
+ if (!di)
+ return;
+
+ resourced_appinfo_put(di->ai);
+ free(di);
+}
+
+
+static int decision_item_update(struct decision_item *di)
+{
+ const struct decision_module *dm;
+ struct decision_table *dt;
+ void *info;
+
+ if (!di || !di->ai) {
+ _E("invalid parameter decision item or null appinfo");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pthread_mutex_lock(&decision_list_mutex);
+ dt = decision_table_find(di->ai->appid, di->ai->pkgname);
+ if (!dt) {
+ _E("there is no decision table for %s", di->ai->appid);
+ pthread_mutex_unlock(&decision_list_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+ pthread_mutex_unlock(&decision_list_mutex);
+
+ info = decision_info_find(dt, di->type);
+ if (!info) {
+ _E("there is no info for type = %d", di->type);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* call sub module's update function */
+ dm = decision_module_find(di->type);
+ if (!dm) {
+ _E("invalid module index = %d", di->type);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dm->update(di, info);
+
+ dt->updated = 1;
+ _D("update item for module %d for appid %s", dm->type, dt->ai->appid);
+
+ decision_item_free(di);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int decision_queue_item_insert(struct decision_item *di)
+{
+ if (!di)
+ return RESOURCED_ERROR_FAIL;
+
+ pthread_mutex_lock(&decision_queue_mutex);
+ g_queue_push_tail(decision_queue, (gpointer)di);
+ pthread_mutex_unlock(&decision_queue_mutex);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int decision_update(void)
+{
+ struct decision_item *di;
+ int ret;
+
+ pthread_mutex_lock(&decision_queue_mutex);
+ while (!g_queue_is_empty(decision_queue)) {
+ di = g_queue_pop_head(decision_queue);
+
+ ret = decision_item_update(di);
+
+ if (ret < 0) {
+ _E("fail to update item");
+ pthread_mutex_unlock(&decision_queue_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ pthread_mutex_unlock(&decision_queue_mutex);
+ _D("finish update deicion item");
+ return RESOURCED_ERROR_NONE;
+}
+
+static void decision_entry_read(const struct filemap_info *fi)
+{
+ if (!fi)
+ return;
+
+ _D("read: name = %s, value = %s", fi->key, fi->value);
+}
+
+static int decision_table_write(struct decision_table *dt)
+{
+ int i, size = 0;
+ /* buffer includes '\n' and null */
+ char buf[DECISION_BUF_MAX] = {0, };
+ char result_buf[DECISION_BUF_MAX] = {0, };
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (!dt) {
+ _E("decision table is null or appinfo is null");
+ return RESOURCED_ERROR_FAIL;
+ }
+ assert(dt->ai);
+
+ if (!dt->updated)
+ return ret;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm = decision_module_find(i);
+ void *info = decision_info_find(dt, i);
+
+ if (!dm || !info) {
+ _E("invalid decision module or decision info");
+ continue;
+ }
+
+ dm->write(info, result_buf, DECISION_BUF_MAX);
+
+ size += snprintf(buf + size, DECISION_BUF_MAX - size, "\t%s",
+ result_buf);
+
+ if (size >= DECISION_BUF_MAX) {
+ _E("write buffer size exceeded size = %d", size);
+ break;
+ }
+ }
+
+ ret = filemap_write(fm, dt->ai->appid, buf, &(dt->offset));
+ if (ret < 0)
+ _E("cannot write buf %s for %s", buf, dt->ai->appid);
+
+ _I("decision write buf: %s", buf);
+ dt->updated = 0;
+
+ return ret;
+}
+
+static int decision_write(void)
+{
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ struct decision_table *dt;
+ int ret;
+
+ g_hash_table_iter_init(&iter, decision_app_list);
+
+ pthread_mutex_lock(&decision_list_mutex);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ dt = (struct decision_table *)value;
+
+ ret = decision_table_write(dt);
+ if (ret < 0)
+ _E("decision table write failed for %s", key);
+ }
+
+ pthread_mutex_unlock(&decision_list_mutex);
+
+ filemap_foreach_read(fm, filemap_root_node(fm), decision_entry_read);
+
+ return ret;
+}
+
+static int decision_app_terminated_cb(void *data)
+{
+ struct proc_status *ps = (struct proc_status *)data;
+ struct decision_table *dt;
+ int ret;
+
+ if (!ps || !ps->pai || !ps->pai->program)
+ return RESOURCED_ERROR_FAIL;
+
+ pthread_mutex_lock(&decision_list_mutex);
+
+ dt = decision_table_find(ps->appid, ps->pai->program->pkgname);
+
+ if (!dt) {
+ pthread_mutex_unlock(&decision_list_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = decision_table_write(dt);
+ if (ret < 0)
+ _D("decision table write failed for %s", ps->appid);
+
+ g_hash_table_remove(decision_app_list, (gconstpointer)ps->appid);
+
+ pthread_mutex_unlock(&decision_list_mutex);
+ _I("%s table is removed from decision table", ps->appid);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void *decision_update_thread(void *arg)
+{
+ int ret;
+
+ setpriority(PRIO_PROCESS, 0, DECISION_PRIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of writing decision result.
+ */
+ ret = pthread_mutex_lock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&decisions[DECISION_THREAD_UPDATE].cond,
+ &decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision update thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret)
+ _E("decision update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ decision_update();
+
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision update thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* now our thread finishes - cleanup tid */
+ decisions[DECISION_THREAD_UPDATE].thread = (int)NULL;
+
+ return NULL;
+}
+
+static void *decision_write_thread_main(void *arg)
+{
+ int ret;
+
+ setpriority(PRIO_PROCESS, 0, DECISION_PRIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of writing decision result.
+ */
+ ret = pthread_mutex_lock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&decisions[DECISION_THREAD_WRITE].cond,
+ &decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret)
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ decision_write();
+
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* now our thread finishes - cleanup tid */
+ decisions[DECISION_THREAD_WRITE].thread = (int)NULL;
+
+ return NULL;
+}
+
+static int decision_thread_create(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ pthread_mutex_init(&decisions[DECISION_THREAD_UPDATE].mutex, NULL);
+ pthread_cond_init(&decisions[DECISION_THREAD_UPDATE].cond, NULL);
+
+ /* initialize decision_update_thread */
+ if (!decisions[DECISION_THREAD_UPDATE].thread) {
+ ret = pthread_create(&decisions[DECISION_THREAD_UPDATE].thread, NULL,
+ (void *)decision_update_thread, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for decision_update_thread_main failed, %d\n", ret);
+ decisions[DECISION_THREAD_UPDATE].thread = (int)NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+ _D("pthread creation for decision update success");
+ pthread_detach(decisions[DECISION_THREAD_UPDATE].thread);
+ }
+ _I("decision update thread %u is running",
+ decisions[DECISION_THREAD_UPDATE].thread);
+
+
+ pthread_mutex_init(&decisions[DECISION_THREAD_WRITE].mutex, NULL);
+ pthread_cond_init(&decisions[DECISION_THREAD_WRITE].cond, NULL);
+
+ /* initialize decision_write_thread */
+ if (!decisions[DECISION_THREAD_WRITE].thread) {
+ ret = pthread_create(&decisions[DECISION_THREAD_WRITE].thread, NULL,
+ (void *)decision_write_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for decision_write_thread failed, %d\n", ret);
+ decisions[DECISION_THREAD_WRITE].thread = (int)NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+ _D("pthread creation for decision write success");
+ pthread_detach(decisions[DECISION_THREAD_WRITE].thread);
+ }
+ _I("decision update thread %u is running",
+ decisions[DECISION_THREAD_WRITE].thread);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int decision_update_start(void)
+{
+ int ret;
+
+ _D("decision update callback function start");
+
+ /* signal to decision update thread */
+ ret = pthread_mutex_trylock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pthread_cond_signal(&decisions[DECISION_THREAD_UPDATE].cond);
+ _I("send signal to decision update thread");
+ pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static Eina_Bool decision_send_signal_to_write(void *data)
+{
+ int ret;
+
+ _D("decision write callback function start");
+
+ /* signal to decision write thread */
+ ret = pthread_mutex_trylock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ pthread_cond_signal(&decisions[DECISION_THREAD_WRITE].cond);
+ _I("send signal to decision write thread");
+ pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static int decision_init(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ decision_app_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ decision_table_free);
+ if (!decision_app_list) {
+ _E("fail to allocate decision app list");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ decision_queue = g_queue_new();
+
+ if (!decision_queue) {
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_queue_init(decision_queue);
+
+ ret = filemap_new(&fm, DECISION_FILE_PATH, DECISION_FILE_SIZE, 1);
+
+ if (ret < 0) {
+ _E("fail filemap_init");
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ g_queue_free(decision_queue);
+ decision_queue = NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ register_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
+ decision_app_terminated_cb);
+
+ if (decision_write_timer == NULL) {
+ _E("decision write timer start");
+ decision_write_timer = ecore_timer_add(DECISION_WRITE_INTERVAL,
+ decision_send_signal_to_write, (void *)NULL);
+ }
+
+ ret = decision_thread_create();
+
+ return ret;
+}
+
+
+static int decision_exit(void)
+{
+ if (decision_app_list) {
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ }
+
+ if (decision_queue) {
+ g_queue_free(decision_queue);
+ decision_queue = NULL;
+ }
+
+ ecore_timer_del(decision_write_timer);
+ decision_write_timer = NULL;
+
+ unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
+ decision_app_terminated_cb);
+
+ filemap_destroy(fm);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int decision_module_register(const struct decision_module *dm)
+{
+ if (!dm) {
+ _E("parameter decision module is NULL");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (num_decisions == 0)
+ decision_init();
+
+ if (decision_module_find(dm->type)) {
+ _E("%d is already exist", dm->type);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ decision_modules[dm->type] = dm;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int decision_module_unregister(const struct decision_module *dm)
+{
+ if (!dm) {
+ _E("parameter decision module is NULL");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ decision_modules[dm->type] = NULL;
+
+ num_decisions--;
+ if (num_decisions == 0)
+ decision_exit();
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+HEART_DEBUG=$1/heart
+/bin/mkdir -p ${HEART_DEBUG}
+/bin/cp -rf /opt/usr/dbspace/.resourced-logging-leveldb/ ${HEART_DEBUG}
+/bin/cp -rf /opt/usr/dbspace/.resourced-logging.db ${HEART_DEBUG}
+/bin/cp -rf /opt/usr/data/heart/ ${HEART_DEBUG}
--- /dev/null
+/*
+ * 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 heart-abnormal.c
+ *
+ * @desc heart abnormal module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "proc-common.h"
+#include "notifier.h"
+#include "resourced.h"
+#include "edbus-handler.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+
+#define ABNORMAL_NAME "abnormal"
+#define ABNORMAL_DATA_MAX 1024
+#define ABNORMAL_CHECK_NUM 10
+
+enum abnormal_type {
+ FC = 0,
+ ANR = 1,
+ ABNORMAL_TYPE_MAX,
+};
+
+struct heart_abnormal_table {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ time_t time;
+ int count[ABNORMAL_TYPE_MAX];
+};
+
+static GHashTable *heart_abnormal_list;
+static pthread_mutex_t heart_abnormal_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void heart_abnormal_fill_array(struct logging_table_form *entry, void *data)
+{
+ int ret, i;
+ unsigned int type;
+ struct heart_abnormal_table *table = NULL;
+ GHashTable *list = (GHashTable *)data;
+
+ sscanf((char *)entry->data, "%*s %*s %u ", &type);
+ if (type >= ABNORMAL_TYPE_MAX) {
+ _E("wrong abnormal type, %u", type);
+ return;
+ }
+ ret = pthread_mutex_lock(&heart_abnormal_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+ table = g_hash_table_lookup(list, entry->appid);
+ if (!table) {
+ table = malloc(sizeof(struct heart_abnormal_table));
+ if (!table) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", entry->appid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ goto unlock_exit;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", entry->pkgid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ goto unlock_exit;
+ }
+ table->time = entry->time;
+
+ for (i = 0; i < ABNORMAL_TYPE_MAX; i++)
+ table->count[i] = 0;
+
+ table->count[type] = 0;
+ g_hash_table_insert(list, (gpointer)table->appid, (gpointer)table);
+ }
+ table->count[type]++;
+
+unlock_exit:
+ ret = pthread_mutex_unlock(&heart_abnormal_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ }
+}
+
+static void heart_abnormal_launch_popup(char *appid, int count)
+{
+ int ret;
+ char num[10];
+ char _appid[MAX_APPID_LENGTH];
+ char *param[6];
+
+ /* Launch malfunction system popup */
+ param[0] = "_SYSPOPUP_CONTENT_";
+ param[1] = "malfunction_notifier";
+ param[2] = "_ERRORS_";
+ snprintf(num, 10, "%d", count);
+ param[3] = num;
+ param[4] = "_APPID_";
+ snprintf(_appid, MAX_APPID_LENGTH, "%s", appid);
+ param[5] = _appid;
+ _D("appid %s, count %d", appid, count);
+
+ ret = dbus_method_async("org.tizen.system.popup",
+ "/Org/Tizen/System/Popup/System",
+ "org.tizen.system.popup.System",
+ "MalfunctionNotifierLaunch", "ssssss", param);
+ if (ret < 0)
+ _E("Failed to launch MalfunctionNotifier");
+ else
+ _I("MalfunctionNotifierLaunch Success");
+}
+
+static void heart_abnormal_process_crashed(void *data, DBusMessage *msg)
+{
+ int ret, notify, count;
+ gpointer key, value;
+ time_t curtime, starttime;
+ GHashTableIter hiter;
+ char *process_name, *exepath, *appid, *pkgid;
+ char info[ABNORMAL_DATA_MAX];
+ struct heart_abnormal_table *table = NULL;
+
+ ret = dbus_message_is_signal(msg, CRASH_INTERFACE_CRASH, PROCESS_CRASHED);
+ if (!ret) {
+ _E("dbus_message_is_signal error");
+ return;
+ }
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &process_name,
+ DBUS_TYPE_STRING, &exepath, DBUS_TYPE_STRING, &appid,
+ DBUS_TYPE_STRING, &pkgid, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return;
+ }
+ curtime = time(NULL);
+ starttime = curtime - 604800;
+ if (starttime < 0)
+ starttime = 0;
+
+ if (g_hash_table_size(heart_abnormal_list))
+ g_hash_table_remove_all(heart_abnormal_list);
+
+ logging_read_foreach(ABNORMAL_NAME, appid, NULL, starttime, curtime,
+ heart_abnormal_fill_array, heart_abnormal_list);
+
+ g_hash_table_iter_init(&hiter, heart_abnormal_list);
+
+ count = 0;
+ while (g_hash_table_iter_next(&hiter, &key, &value)) {
+ table = (struct heart_abnormal_table *)value;
+ if (!table)
+ break;
+ count += table->count[FC];
+ }
+
+ notify = 0;
+ if (count > ABNORMAL_CHECK_NUM) {
+ heart_abnormal_launch_popup(appid, count);
+ notify = 1;
+ }
+
+ g_hash_table_remove_all(heart_abnormal_list);
+
+ snprintf(info, sizeof(info), "%s %s %d %d ", process_name, exepath, notify, FC);
+ _D("info : %s %d", info, count);
+ ret = logging_write(ABNORMAL_NAME, appid, pkgid, time(NULL), info);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to logging_write %s", info);
+ }
+}
+
+static int heart_abnormal_anr(void *data)
+{
+ int ret;
+ char info[ABNORMAL_DATA_MAX];
+ struct proc_status *ps = (struct proc_status *)data;
+ char *appid, *pkgid;
+
+ ret = proc_get_id_info(ps, &appid, &pkgid);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to proc_get_id_info");
+ return ret;
+ }
+ snprintf(info, sizeof(info), "%d ANR %d ", ps->pid, ANR);
+ _D("info : %s", info);
+ ret = logging_write(ABNORMAL_NAME, appid, pkgid, time(NULL), info);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to logging_write %s", info);
+ }
+ return ret;
+}
+
+static void heart_abnormal_free_value(gpointer value)
+{
+ struct heart_abnormal_table *table =
+ (struct heart_abnormal_table *)value;
+
+ if (!table)
+ return;
+
+ free(table);
+}
+
+static DBusMessage *edbus_heart_get_abnormal_data(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int type, period, ret, count, i;
+ time_t starttime;
+ char *appid;
+ gpointer key, value;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ GHashTableIter hiter;
+ struct heart_abnormal_table *table = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &type,
+ DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ if (type < 0 || ABNORMAL_TYPE_MAX < type) {
+ _E("Wrong message arguments! %d", type);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ starttime = time(NULL);
+ switch (period) {
+ case DATA_LATEST:
+ starttime = 0;
+ break;
+ case DATA_3HOUR:
+ starttime -= 10800;
+ break;
+ case DATA_6HOUR:
+ starttime -= 21600;
+ break;
+ case DATA_12HOUR:
+ starttime -= 43200;
+ break;
+ case DATA_1DAY:
+ starttime -= 86400;
+ break;
+ case DATA_1WEEK:
+ starttime -= 604800;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (starttime < 0)
+ starttime = 0;
+
+ if (g_hash_table_size(heart_abnormal_list))
+ g_hash_table_remove_all(heart_abnormal_list);
+
+ logging_read_foreach(ABNORMAL_NAME, NULL, NULL, starttime, time(NULL),
+ heart_abnormal_fill_array, heart_abnormal_list);
+
+ reply = dbus_message_new_method_return(msg);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ ret = pthread_mutex_lock(&heart_abnormal_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ g_hash_table_iter_init(&hiter, heart_abnormal_list);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(si)", &arr);
+
+ while (g_hash_table_iter_next(&hiter, &key, &value)) {
+ DBusMessageIter sub;
+ table = (struct heart_abnormal_table *)value;
+ if (!table)
+ break;
+ count = 0;
+ appid = table->appid;
+
+ if (type == ABNORMAL_TYPE_MAX) {
+ for (i = 0; i < ABNORMAL_TYPE_MAX; i++)
+ count += table->count[i];
+ } else
+ count += table->count[type];
+
+ if (!count)
+ continue;
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &count);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&iter, &arr);
+
+ ret = pthread_mutex_unlock(&heart_abnormal_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ g_hash_table_remove_all(heart_abnormal_list);
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { "GetAbnormalData", "ii", "a(si)", edbus_heart_get_abnormal_data },
+};
+
+static int heart_abnormal_init(void *data)
+{
+ int ret;
+
+ ret = logging_module_init(ABNORMAL_NAME, ONE_WEEK, HALF_HOUR, NULL, 0);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("logging module init failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed",
+ RESOURCED_PATH_LOGGING);
+ }
+ ret = register_edbus_signal_handler(CRASH_PATH_CRASH,
+ CRASH_INTERFACE_CRASH, PROCESS_CRASHED,
+ heart_abnormal_process_crashed, NULL);
+ if (ret < 0) {
+ _E("Failed to add a capacity status signal handler");
+ }
+ heart_abnormal_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ heart_abnormal_free_value);
+
+ register_notifier(RESOURCED_NOTIFIER_APP_ANR, heart_abnormal_anr);
+ _D("heart abnormal init finished");
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_abnormal_exit(void *data)
+{
+ if (heart_abnormal_list)
+ g_hash_table_destroy(heart_abnormal_list);
+ logging_module_exit();
+ unregister_notifier(RESOURCED_NOTIFIER_APP_ANR, heart_abnormal_anr);
+ _D("heart abnormal exit");
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_abnormal_ops = {
+ .name = "ABNORMAL",
+ .init = heart_abnormal_init,
+ .exit = heart_abnormal_exit,
+};
+HEART_MODULE_REGISTER(&heart_abnormal_ops)
--- /dev/null
+/*
+ * 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 heart-appopt.c
+ *
+ * @desc heart application optimization module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <Ecore.h>
+#include <stdbool.h>
+#include <sqlite3.h>
+#include <time.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "notifier.h"
+#include "heart.h"
+#include "heart-common.h"
+#include "proc-common.h"
+#include "edbus-handler.h"
+
+#define APPOPT_DB_FILE_NAME "/opt/usr/dbspace/.resourced-appopt.db"
+#define APP_NAMES_TABLE "appnames"
+#define APP_OPTS_TABLE "appopts"
+#define QUERY_CREATE_APPNAMES "CREATE TABLE IF NOT EXISTS "APP_NAMES_TABLE" (appkey INTEGER PRIMARY KEY AUTOINCREMENT, appname TEXT NOT NULL UNIQUE);"
+#define QUERY_CREATE_APPOPTS "CREATE TABLE IF NOT EXISTS "APP_OPTS_TABLE" (appkey INTEGER PRIMARY KEY, last_used INT NOT NULL, cur_opt INT NOT NULL, to_be_opt INT NOT NULL, FOREIGN KEY(appkey) REFERENCES "APP_NAMES_TABLE"(appkey));"
+#define QUERY_INSERT_APPNAME "INSERT OR IGNORE INTO "APP_NAMES_TABLE"(appname) VALUES ('%s');"
+#define QUERY_INSERT_APPOPTS "REPLACE INTO "APP_OPTS_TABLE" VALUES ('%d','%d','%d','%d');"
+#define QUERY_DELETE_ENTRY "DELETE FROM %s WHERE appkey = %d;"
+#define QUERY_SELECT_APPOPTS "SELECT appname,last_used,cur_opt,to_be_opt FROM "APP_NAMES_TABLE","APP_OPTS_TABLE" WHERE "APP_NAMES_TABLE".appkey = "APP_OPTS_TABLE".appkey;"
+#define QUERY_SELECT_APPNAME "SELECT * FROM "APP_NAMES_TABLE " WHERE appname = '%s';"
+#define APPOPT_DATA_MAX 1024
+#define SQLITE_BUSY_TIMEOUT 50000
+
+#define PKGMGR_STATUS_OBJECT_PATH "/org/tizen/pkgmgr_status"
+#define PKGMGR_STATUS_INTERFACE_NAME "org.tizen.pkgmgr_status"
+#define PKGMGR_STATUS_SIGNAL "status"
+
+#define APPOPT_STR_INSTALL "install"
+#define APPOPT_STR_UNINSTALL "uninstall"
+#define APPOPT_STR_START "start"
+#define APPOPT_STR_END "end"
+#define APPOPT_STR_OK "ok"
+
+struct appopt_data {
+ char *appname;
+ int last_used;
+ int cur_opt;
+ int to_be_opt;
+};
+
+enum appopt_cmd_type {
+ APPOPT_CMD_INSERT,
+ APPOPT_CMD_REMOVE,
+ APPOPT_CMD_SELECT,
+};
+
+struct appopt_cmd {
+ enum appopt_cmd_type type;
+ struct appopt_data data;
+};
+
+static pthread_mutex_t heart_appopt_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t heart_appopt_db_cmd_thread = 0;
+static GQueue *queue = NULL;
+GArray *appopt_cache;
+int appopt_cache_ready = 0;
+
+static int heart_appopt_db_cache_entries(sqlite3 *db)
+{
+ sqlite3_stmt *stmt;
+ struct appopt_data data;
+ char buf[APPOPT_DATA_MAX] = {0, };
+ int ret;
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_SELECT_APPOPTS);
+
+ ret = sqlite3_prepare_v2(db, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("Failed to prepare query %s", sqlite3_errmsg(db));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ switch (ret) {
+ case SQLITE_ROW:
+ if (asprintf(&data.appname, "%s", (char *) sqlite3_column_text(stmt, 0)) < 0) {
+ _E("asprintf failed");
+ ret = RESOURCED_ERROR_OUT_OF_MEMORY;
+ goto error_malloc;
+ }
+
+ data.last_used = sqlite3_column_int(stmt, 1);
+ data.cur_opt = sqlite3_column_int(stmt, 2);
+ data.to_be_opt = sqlite3_column_int(stmt, 3);
+ g_array_append_val(appopt_cache, data);
+ break;
+ case SQLITE_DONE:
+ _D("SQLITE_DONE");
+ break;
+ case SQLITE_ERROR:
+ /* FALLTHROUGH */
+ _E("sqlite3_step failed %s", sqlite3_errmsg(db));
+ default:
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ ret = RESOURCED_ERROR_DB_FAILED;
+ break;
+ }
+ } while (ret == SQLITE_ROW);
+
+ sqlite3_finalize(stmt);
+
+ return RESOURCED_ERROR_NONE;
+
+error_malloc:
+ sqlite3_finalize(stmt);
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ return ret;
+}
+
+static int heart_appopt_db_get_appkey(sqlite3 *db, char *appname, int *appkey)
+{
+ sqlite3_stmt *stmt;
+ char buf[APPOPT_DATA_MAX] = {0, };
+ int ret;
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_SELECT_APPNAME, appname);
+
+ ret = sqlite3_prepare_v2(db, buf, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("Failed to prepare query %s", sqlite3_errmsg(db));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ ret = sqlite3_step(stmt);
+
+ switch (ret) {
+ case SQLITE_ROW:
+ *appkey = sqlite3_column_int(stmt, 0);
+ ret = RESOURCED_ERROR_NONE;
+ break;
+ case SQLITE_DONE:
+ _E("Appkey not found for %s", appname);
+ ret = RESOURCED_ERROR_NO_DATA;
+ break;
+ case SQLITE_ERROR:
+ /* FALLTHROUGH */
+ _E("sqlite3_step failed %s", sqlite3_errmsg(db));
+ default:
+ ret = RESOURCED_ERROR_DB_FAILED;
+ break;
+ }
+
+ sqlite3_finalize(stmt);
+
+ return ret;
+}
+
+static int heart_appopt_db_open_transaction(sqlite3 *db)
+{
+ char *sqlite3_error_msg = NULL;
+
+ if (sqlite3_exec(db, "PRAGMA journal_mode = PERSIST", NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"PRAGMA journal_mode = PERSIST\") failed! -> %s", sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_exec(db, "BEGIN EXCLUSIVE", NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"BEGIN EXCLUSIVE\") failed! -> %s", sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_appopt_db_insert_entry(sqlite3 *db, struct appopt_data *data)
+{
+ char buf[APPOPT_DATA_MAX] = {0, };
+ char *sqlite3_error_msg = NULL;
+ int appkey, ret;
+
+ if (!data) {
+ _E("No data data found.");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ ret = heart_appopt_db_open_transaction(db);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ /* Make sure appname is present in the appnames dictionary */
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_INSERT_APPNAME, data->appname);
+
+ if (sqlite3_exec(db, buf, NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"%s\") failed! -> %s", buf, sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db;
+ }
+
+ /* Get the appkey for the app the opts are to be added for */
+
+ ret = heart_appopt_db_get_appkey(db, data->appname, &appkey);
+ if (ret != RESOURCED_ERROR_NONE)
+ goto error_db;
+
+ /* Insert/update application optimization settings */
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_INSERT_APPOPTS, appkey,
+ data->last_used, data->cur_opt, data->to_be_opt);
+
+ if (sqlite3_exec(db, buf, NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"%s\") failed! -> %s", buf, sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db;
+ }
+
+ if (sqlite3_exec(db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) {
+ _E("sqlite3_exec(\"COMMIT\") failed!");
+ goto error_db;
+ }
+
+ return RESOURCED_ERROR_NONE;
+
+error_db:
+ if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK)
+ _E("sqlite3_exec(\"ROLLBACK\") failed!");
+
+ return RESOURCED_ERROR_DB_FAILED;
+}
+
+static int heart_appopt_db_remove_entry(sqlite3 *db, struct appopt_data *data)
+{
+ char buf[APPOPT_DATA_MAX] = {0, };
+ char *sqlite3_error_msg = NULL;
+ int appkey, ret;
+
+ if (!data) {
+ _E("No data data found.");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ /* Get the appkey for the app the opts are to be added for */
+
+ ret = heart_appopt_db_get_appkey(db, data->appname, &appkey);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ ret = heart_appopt_db_open_transaction(db);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ /* Delete app optimization data */
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_DELETE_ENTRY, APP_OPTS_TABLE, appkey);
+
+ if (sqlite3_exec(db, buf, NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"%s\") failed! -> %s", buf, sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db;
+ }
+
+ /* Delete app name data */
+
+ snprintf(buf, APPOPT_DATA_MAX, QUERY_DELETE_ENTRY, APP_NAMES_TABLE, appkey);
+
+ if (sqlite3_exec(db, buf, NULL, NULL, &sqlite3_error_msg) != SQLITE_OK) {
+ _E("sqlite3_exec(\"%s\") failed! -> %s", buf, sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db;
+ }
+
+ if (sqlite3_exec(db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) {
+ _E("sqlite3_exec(\"COMMIT\") failed!");
+ goto error_db;
+ }
+
+ return RESOURCED_ERROR_NONE;
+
+error_db:
+ if (sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK)
+ _E("sqlite3_exec(\"ROLLBACK\") failed!");
+
+ return RESOURCED_ERROR_DB_FAILED;
+}
+
+static int appopt_db_busy(void * UNUSED user, int attempts)
+{
+ _E("DB locked by another process, attempts number %d", attempts);
+
+ usleep(SQLITE_BUSY_TIMEOUT); /* wait for a half second*/
+ return 1;
+}
+
+static void heart_appopt_execute_db_cmd(struct appopt_cmd *cmd)
+{
+ sqlite3 *appopt_db;
+
+ if (sqlite3_open(APPOPT_DB_FILE_NAME, &appopt_db) != SQLITE_OK) {
+ _E("Can't open database %s: %s", APPOPT_DB_FILE_NAME,
+ sqlite3_errmsg(appopt_db));
+ sqlite3_close(appopt_db);
+ return;
+ }
+
+ if (sqlite3_exec(appopt_db, "PRAGMA locking_mode = NORMAL", 0, 0, 0) != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(appopt_db));
+ sqlite3_close(appopt_db);
+ return;
+ } else {
+ if (sqlite3_busy_handler(appopt_db, appopt_db_busy, NULL) != SQLITE_OK)
+ _E("Couldn't set busy handler!");
+ }
+
+ if (sqlite3_exec(appopt_db, "PRAGMA foreign_keys = ON;", 0, 0, 0) != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(appopt_db));
+ sqlite3_close(appopt_db);
+ return;
+ }
+
+ switch (cmd->type) {
+ case APPOPT_CMD_INSERT:
+ if (heart_appopt_db_insert_entry(appopt_db, &cmd->data) != RESOURCED_ERROR_NONE) {
+ _E("Appopt data insertion failed");
+ } else {
+ /* invalidate cache on db update success */
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ appopt_cache_ready = 0;
+ }
+ break;
+ case APPOPT_CMD_REMOVE:
+ if (heart_appopt_db_remove_entry(appopt_db, &cmd->data) != RESOURCED_ERROR_NONE) {
+ _E("Query execution failed");
+ } else {
+ /* invalidate cache on db remove success */
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ appopt_cache_ready = 0;
+ }
+ break;
+ case APPOPT_CMD_SELECT:
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ appopt_cache_ready = 0;
+ if (heart_appopt_db_cache_entries(appopt_db) != RESOURCED_ERROR_NONE) {
+ _E("Failed to fetch rows");
+ g_array_remove_range(appopt_cache, 0, appopt_cache->len);
+ } else {
+ appopt_cache_ready = 1;
+ }
+ break;
+ default:
+ _E("Unknown appopt command");
+ break;
+ }
+
+ sqlite3_close(appopt_db);
+}
+
+static void *heart_appopt_db_cmd_thread_main(void *arg)
+{
+ struct appopt_cmd *cmd;
+ int ret;
+
+ do {
+ ret = pthread_mutex_lock(&heart_appopt_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ cmd = g_queue_pop_head(queue);
+ if (!cmd)
+ break;
+
+ heart_appopt_execute_db_cmd(cmd);
+
+ pthread_mutex_unlock(&heart_appopt_mutex);
+
+ free(cmd->data.appname);
+ free(cmd);
+ } while (1);
+
+ heart_appopt_db_cmd_thread = 0;
+ pthread_mutex_unlock(&heart_appopt_mutex);
+ pthread_exit((void *)0);
+}
+
+static int heart_appopt_enqueue_db_cmd(struct appopt_cmd *cmd)
+{
+ pthread_attr_t attr;
+ int ret;
+
+ ret = pthread_mutex_lock(&heart_appopt_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_queue_push_tail(queue, cmd);
+
+ if (heart_appopt_db_cmd_thread) {
+ pthread_mutex_unlock(&heart_appopt_mutex);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ ret = pthread_attr_init(&attr);
+ if (ret < 0) {
+ _E("Failed to initialize pthread attributes, %d", ret);
+ pthread_mutex_unlock(&heart_appopt_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret < 0) {
+ _E("Failed to set detached state, %d", ret);
+ pthread_mutex_unlock(&heart_appopt_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_create(&heart_appopt_db_cmd_thread, &attr,
+ heart_appopt_db_cmd_thread_main, NULL);
+ if (ret < 0) {
+ _E("pthread creation for heart_storage_verifying_thread_main failed, %d", ret);
+ pthread_mutex_unlock(&heart_appopt_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pthread_mutex_unlock(&heart_appopt_mutex);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_appopt_request_appopt_list()
+{
+ struct appopt_cmd *cmd;
+
+ cmd = calloc(1, sizeof(struct appopt_cmd));
+ if (!cmd) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+ cmd->type = APPOPT_CMD_SELECT;
+
+ if (heart_appopt_enqueue_db_cmd(cmd)) {
+ _E("Failed to enqueue db query!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_appopt_enqueue_insert_cmd(char *appname, int last_used,
+ int cur_opt, int to_be_opt)
+{
+ struct appopt_cmd *cmd;
+ struct appopt_data *data;
+ int ret;
+
+ cmd = malloc(sizeof(struct appopt_cmd));
+ if (!cmd) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ cmd->type = APPOPT_CMD_INSERT;
+ data = &cmd->data;
+ data->last_used = last_used;
+ data->cur_opt = cur_opt;
+ data->to_be_opt = to_be_opt;
+
+ if (asprintf(&data->appname, "%s", appname) < 0) {
+ free(cmd);
+ _E("asprintf failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ ret = heart_appopt_enqueue_db_cmd(cmd);
+ if (ret != RESOURCED_ERROR_NONE) {
+ free(data->appname);
+ free(cmd);
+ _E("Failed to enqueue db query!");
+ return ret;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_appopt_enqueue_remove_cmd(char *appname)
+{
+ struct appopt_cmd *cmd;
+ struct appopt_data *data;
+ int ret;
+
+ cmd = malloc(sizeof(struct appopt_cmd));
+ if (!cmd) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ cmd->type = APPOPT_CMD_REMOVE;
+ data = &cmd->data;
+
+ if (asprintf(&data->appname, "%s", appname) < 0) {
+ free(cmd);
+ _E("asprintf failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ ret = heart_appopt_enqueue_db_cmd(cmd);
+ if (ret != RESOURCED_ERROR_NONE) {
+ free(data->appname);
+ free(cmd);
+ _E("Failed to enqueue db query!");
+ return ret;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static DBusMessage *edbus_request_appopt_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ if (appopt_cache->len) {
+ _I("Rows already cached.");
+ return dbus_message_new_method_return(msg);
+ }
+
+ if (heart_appopt_request_appopt_list() != RESOURCED_ERROR_NONE)
+ _I("Failed to request appopt list.");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *edbus_get_appopt_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ int i;
+
+ if (!appopt_cache_ready) {
+ _I("Cache not ready, execute RequestAppOptList beforehand.");
+ return dbus_message_new_method_return(msg);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(siii)", &arr);
+
+ for (i = 0; i < appopt_cache->len; i++) {
+ DBusMessageIter sub;
+ struct appopt_data *data;
+
+ data = &g_array_index(appopt_cache, struct appopt_data, i);
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &data->appname);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &data->last_used);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &data->cur_opt);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &data->to_be_opt);
+
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr);
+
+ return reply;
+}
+
+static DBusMessage *edbus_insert_appopt(E_DBus_Object *obj, DBusMessage *msg)
+{
+ char *appname;
+ int last_used, cur_opt, to_be_opt, ret;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appname,
+ DBUS_TYPE_INT32, &last_used, DBUS_TYPE_INT32, &cur_opt,
+ DBUS_TYPE_INT32, &to_be_opt, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return dbus_message_new_method_return(msg);
+ }
+
+ heart_appopt_enqueue_insert_cmd(appname, last_used,
+ cur_opt, to_be_opt);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *edbus_remove_appopt(E_DBus_Object *obj, DBusMessage *msg)
+{
+ char *appname;
+ int ret;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appname,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return dbus_message_new_method_return(msg);
+ }
+
+ heart_appopt_enqueue_remove_cmd(appname);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { "InsertAppOpt", "siii", NULL, edbus_insert_appopt },
+ { "RemoveAppOpt", "s", NULL, edbus_remove_appopt },
+ { "RequestAppOptList", NULL, NULL, edbus_request_appopt_list },
+ { "GetAppOptList", NULL, "a(siii)", edbus_get_appopt_list },
+};
+
+static void destroy_array_element(gpointer data)
+{
+ struct appopt_data *d = (struct appopt_data*) data;
+
+ if (d->appname)
+ free(d->appname);
+}
+
+static int heart_appopt_init_db(void)
+{
+ sqlite3 *appopt_db;
+ char *sqlite3_error_msg = NULL;
+ char buf[APPOPT_DATA_MAX] = {0, };
+ int ret;
+
+ ret = sqlite3_open(APPOPT_DB_FILE_NAME, &appopt_db);
+ if (ret != SQLITE_OK) {
+ _E("Can't open database %s: %s", APPOPT_DB_FILE_NAME,
+ sqlite3_errmsg(appopt_db));
+ goto error_db_open;
+ }
+
+ snprintf(buf, APPOPT_DATA_MAX, "%s", QUERY_CREATE_APPNAMES);
+
+ ret = sqlite3_exec(appopt_db, buf, NULL, NULL, &sqlite3_error_msg);
+ if (ret != SQLITE_OK) {
+ _E("create failed", sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db_open;
+ }
+
+ snprintf(buf, APPOPT_DATA_MAX, "%s", QUERY_CREATE_APPOPTS);
+
+ ret = sqlite3_exec(appopt_db, buf, NULL, NULL, &sqlite3_error_msg);
+ if (ret != SQLITE_OK) {
+ _E("create failed", sqlite3_error_msg);
+ sqlite3_free(sqlite3_error_msg);
+ goto error_db_open;
+ }
+
+ return RESOURCED_ERROR_NONE;
+
+error_db_open:
+ sqlite3_close(appopt_db);
+ return RESOURCED_ERROR_DB_FAILED;
+}
+
+static void heart_appopt_pkgmgr_status(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ struct timeval tv;
+ char *req_id, *pkgtype, *pkgid, *key, *val;
+ static int pkg_install = 0, pkg_uninstall = 0;
+ int ret;
+
+ if (dbus_message_is_signal(msg, PKGMGR_STATUS_INTERFACE_NAME,
+ PKGMGR_STATUS_SIGNAL) == 0) {
+ _D("not a pkgmgr_status signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &req_id,
+ DBUS_TYPE_STRING, &pkgtype, DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &val,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ _D("heart_appopt_pkgmgr_status:"
+ "req_id: %s, pkgtype: %s, pkgid: %s, key: %s, val: %s",
+ req_id, pkgtype, pkgid, key, val);
+
+ if (!strcmp(key, APPOPT_STR_START)) {
+ if (!strcmp(val, APPOPT_STR_INSTALL))
+ pkg_install = 1;
+ else if (!strcmp(val, APPOPT_STR_UNINSTALL))
+ pkg_uninstall = 1;
+ } else if (!strcmp(key, APPOPT_STR_END)) {
+ if (!strcmp(val, APPOPT_STR_OK)) {
+ if (pkg_install) {
+ gettimeofday(&tv, NULL);
+ ret = heart_appopt_enqueue_insert_cmd(pkgid, tv.tv_sec,
+ 0, 0);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Failed to add appopt entry for new package");
+
+ } else if (pkg_uninstall) {
+ ret = heart_appopt_enqueue_remove_cmd(pkgid);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Failed to remove appopt entry for removed package");
+ }
+ }
+ pkg_install = 0;
+ pkg_uninstall = 0;
+ }
+}
+
+static const struct edbus_signal edbus_signals[] = {
+ {PKGMGR_STATUS_OBJECT_PATH, PKGMGR_STATUS_INTERFACE_NAME,
+ PKGMGR_STATUS_SIGNAL, heart_appopt_pkgmgr_status, NULL},
+};
+
+static int heart_appopt_init(void *data)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ ret = pthread_mutex_init(&heart_appopt_mutex, NULL);
+ if (ret < 0) {
+ _E("mutex_init failed %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ queue = g_queue_new();
+ if (!queue) {
+ _E("queue init failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ g_queue_init(queue);
+
+ appopt_cache = g_array_new(FALSE, TRUE, sizeof(struct appopt_data));
+ g_array_set_clear_func(appopt_cache, destroy_array_element);
+
+ ret = heart_appopt_init_db();
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ ret = heart_appopt_request_appopt_list();
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ ret = edbus_add_signals(edbus_signals, ARRAY_SIZE(edbus_signals));
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("DBus signals registration failed.");
+
+ ret = edbus_add_methods(RESOURCED_PATH_APPOPT, edbus_methods, ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("DBus method registration for %s failed.", RESOURCED_PATH_APPOPT);
+
+ return ret;
+}
+
+static int heart_appopt_exit(void *data)
+{
+ g_array_free(appopt_cache, TRUE);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_appopt_ops = {
+ .name = "APPOPT",
+ .init = heart_appopt_init,
+ .exit = heart_appopt_exit,
+};
+HEART_MODULE_REGISTER(&heart_appopt_ops)
--- /dev/null
+/*
+ * 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 heart-battery.c
+ *
+ * @desc heart battery module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "proc-common.h"
+#include "notifier.h"
+#include "resourced.h"
+#include "edbus-handler.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "config-parser.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+
+#define TIZEN_SYSTEM_APPID "org.tizen.system"
+#define TIZEN_SYSTEM_BATTERY_APPID "org.tizen.system.battery.capacity"
+#define BATTERY_NAME "battery"
+#define BATTERY_DATA_MAX 1024
+#define BATTERY_CAPACITY_MAX 512
+#define BATTERY_LINE_MAX 128
+#define BATTERY_CLEAN_MAX 100
+#define BATTERY_HISTORY_DAY_MAX 7
+#define BATTERY_HISTORY_RESET_MAX 5
+#define BATTERY_HISTORY_RESET_CURRENT (BATTERY_HISTORY_RESET_MAX - 1)
+#define BATTERY_HISTORY_SECONDS_MAX DAY_TO_SEC(BATTERY_HISTORY_DAY_MAX)
+#define BATTERY_HISTORY_COUNT_MAX 1000
+#define HEART_BATTERY_UPDATE_INTERVAL HALF_HOUR
+#define HEART_BATTERY_SAVE_INTERVAL HALF_HOUR
+#define HEART_BATTERY_CAPACITY_DATA_FILE HEART_FILE_PATH"/.battery_capacity.dat"
+#define HEART_BATTERY_CONF_SECTION "BATTERY_POWER_MODE"
+#define GET_CHARGER_STATUS "ChargerStatus"
+#define GET_BATTERY_CAPACITY "GetPercent"
+#define CALCULATE_DAY_BASE_TIME(x) ((x / DAY_TO_SEC(1)) * (DAY_TO_SEC(1)))
+#define REMAIN_CAPACITY(x) (100 - x)
+#define BATTERY_PREDICTION_DATA_MIN 5
+#define BATTERY_USAGE_LEARNING -1
+#define CUMUL_WEIGHT (0.8)
+#define TREND_WEIGHT (1 - CUMUL_WEIGHT)
+/*
+ * BATTERY_PREDICTION_LATEST_COUNT must be >= BATTERY_PREDICTION_DATA_MIN
+ */
+#define BATTERY_PREDICTION_LATEST_COUNT 5
+/*
+ * BATTERY_PREDICTION_PERIOD possible values:
+ * DATA_LATEST, DATA_3HOUR, DATA_6HOUR, DATA_12HOUR, DATA_1DAY
+ */
+#define BATTERY_PREDICTION_PERIOD DATA_3HOUR
+
+#define BATTERY_USED_TIME "BATTERY_USED_TIME"
+#define BATTERY_STATUS "BATTERY_STATUS"
+#define BATTERY_RESET_USAGE "BATTERY_RESET_USAGE"
+#define BATTERY_WEEK_DAY_USAGE "BATTERY_WEEK_DAY_USAGE"
+#define BATTERY_LEVEL_USAGE "BATTERY_LEVEL_USAGE"
+#define BATTERY_PREDICTION "BATTERY_PREDICTION"
+
+enum {
+ TA = 0, /* prediction based on total data average */
+ PCB = 1, /* prediction with physiological behaviors */
+ WEEK = 2, /* prediction based on weekly data */
+ COUNT = 3, /* prediction based on last BATTERY_PREDICTION_COUNT number of items */
+ PERIOD = 4, /* prediction based on data from last BATTERY_PREDICTION_PERIOD time */
+ MAX_STRATEGY = 5,
+};
+
+enum {
+ POWER_NORMAL_MODE = 0,
+ POWER_SAVING_MODE = 1,
+ ULTRA_SAVING_MODE = 2,
+ POWER_MODE_MAX = 3,
+};
+
+enum charging_goal {
+ DISCHARGING = 0,
+ CHARGING = 1,
+ MAX_CHARGER_STATE = 2,
+};
+
+enum {
+ BATTERY_LEVEL_LOW = 0, /* 15 ~ 0 */
+ BATTERY_LEVEL_MID = 1, /* 49 ~ 16 */
+ BATTERY_LEVEL_HIGH = 2, /* 50 ~ 100 */
+ BATTERY_LEVEL_MAX = 3,
+};
+
+enum {
+ DEFAULT_MIN = 0,
+ DEFAULT_AVG = 1,
+ DEFAULT_MAX = 2,
+ DEFAULT_VALUE_MAX = 3,
+};
+
+struct battery_used {
+ time_t used_time_sec; /* seconds on battery */
+ time_t last_update_time;
+ int last_charger_status;
+};
+
+struct battery_usage {
+ time_t start_time; /* timestamp when event started */
+ long sec_per_cap[MAX_CHARGER_STATE]; /* seconds per capacity level change */
+ long cap_counter[MAX_CHARGER_STATE]; /* number of capacity level changes */
+};
+
+struct battery_prediction {
+ long sec_per_cap[MAX_STRATEGY]; /* seconds per capacity level change */
+ long cap_counter[MAX_STRATEGY]; /* number of capacity level changes */
+ long time_pred_min[MAX_STRATEGY]; /* time prediction in minutes */
+};
+
+struct battery_status {
+ /* current battery status */
+ int curr_charger_status;
+ int curr_capacity;
+
+ /* current runtime statistics */
+ long curr_run_time_sec[MAX_CHARGER_STATE]; /* seconds since reset */
+ long curr_cap_counter[MAX_CHARGER_STATE]; /* capacity level changes */
+
+ /* wall clock time stamp when last event happened in seconds */
+ time_t last_event_wall_time;
+
+ /*
+ * reset mark is set when battery is charged in over 90% and
+ * charger was disconnected from the device.
+ * We consider then the device as "charged"
+ *
+ * The possible values are 0 and 1 they're swapped to opposite on change.
+ */
+ int reset_mark;
+ time_t reset_mark_timestamp;
+
+ /* usage time from last reset_mark change*/
+ struct battery_usage batt_reset_usage[BATTERY_HISTORY_RESET_MAX];
+
+ /* usage time by week day */
+ struct battery_usage week_day_usage[BATTERY_HISTORY_DAY_MAX];
+
+ /* usage time by user behavior & battery level */
+ struct battery_usage batt_lvl_usage[BATTERY_LEVEL_MAX];
+
+ /* calculated battery prediction */
+ struct battery_prediction prediction[MAX_CHARGER_STATE];
+};
+
+static int default_sec_per_cap[MAX_CHARGER_STATE][DEFAULT_VALUE_MAX] = {
+ { 70, 670, 3600 }, /* DISCHARGING MIN: 70s, AVG: 670s, MAX: 1 hour */
+ { 30, 80, 3600 } /* CHARGING MIN: 30s, AVG: 80s, MAX: 1 hour */
+};
+
+static double default_mode_spc[POWER_MODE_MAX] = {
+ 670, /* POWER_NORMAL_MODE */
+ 750, /* POWER_SAVING_MODE */
+ 1947 /* ULTRA_SAVING_MODE */
+};
+
+static double default_mode_factor[POWER_MODE_MAX] = {
+ 1, /* POWER_NORMAL_MODE */
+ 1.1, /* POWER_SAVING_MODE */
+ 2.88 /* ULTRA_SAVING_MODE */
+};
+
+static struct battery_used batt_used;
+static struct battery_status batt_stat;
+static GSList *capacity_history_list = NULL;
+static pthread_mutex_t heart_battery_mutex = PTHREAD_MUTEX_INITIALIZER;
+static time_t last_file_commit_time;
+static int battery_learning_mode;
+
+inline void heart_battery_set_usage_reset_stime(int history, time_t start_time)
+{
+ batt_stat.batt_reset_usage[history].start_time = start_time;
+}
+
+inline time_t heart_battery_get_usage_reset_stime(int history)
+{
+ return batt_stat.batt_reset_usage[history].start_time;
+}
+
+inline void heart_battery_set_usage_reset(int history, int status, long sec_per_cap, long cap_counter)
+{
+ batt_stat.batt_reset_usage[history].sec_per_cap[status] = sec_per_cap;
+ batt_stat.batt_reset_usage[history].cap_counter[status] = cap_counter;
+}
+
+inline long heart_battery_get_usage_reset_total_time(int history, int status)
+{
+ return batt_stat.batt_reset_usage[history].sec_per_cap[status] * batt_stat.batt_reset_usage[history].cap_counter[status];
+}
+
+inline long heart_battery_get_usage_reset_count(int history, int status)
+{
+ return batt_stat.batt_reset_usage[history].cap_counter[status];
+}
+
+inline void heart_battery_set_usage_level_stime(int level, time_t start_time)
+{
+ batt_stat.batt_lvl_usage[level].start_time = start_time;
+}
+
+inline time_t heart_battery_get_usage_level_stime(int level)
+{
+ return batt_stat.batt_lvl_usage[level].start_time;
+}
+
+inline void heart_battery_set_usage_level(int level, int status, long sec_per_cap, long cap_counter)
+{
+ batt_stat.batt_lvl_usage[level].sec_per_cap[status] = sec_per_cap;
+ batt_stat.batt_lvl_usage[level].cap_counter[status] = cap_counter;
+}
+
+inline long heart_battery_get_usage_level_total_time(int level, int status)
+{
+ return batt_stat.batt_lvl_usage[level].sec_per_cap[status] * batt_stat.batt_lvl_usage[level].cap_counter[status];
+}
+
+inline long heart_battery_get_usage_level_spc(int level, int status)
+{
+ return batt_stat.batt_lvl_usage[level].sec_per_cap[status];
+}
+
+inline long heart_battery_get_usage_level_count(int level, int status)
+{
+ return batt_stat.batt_lvl_usage[level].cap_counter[status];
+}
+
+inline long heart_battery_get_usage_week_total_time(int day, int status)
+{
+ return batt_stat.week_day_usage[day].sec_per_cap[status] * batt_stat.week_day_usage[day].cap_counter[status];
+}
+
+inline long heart_battery_get_usage_week_count(int day, int status)
+{
+ return batt_stat.week_day_usage[day].cap_counter[status];
+}
+
+inline void heart_battery_set_usage_week_stime(int day, time_t start_time)
+{
+ batt_stat.week_day_usage[day].start_time = start_time;
+}
+
+inline time_t heart_battery_get_usage_week_stime(int day)
+{
+ return batt_stat.week_day_usage[day].start_time;
+}
+
+inline int heart_battery_get_learning_mode(void)
+{
+ int i, count = 0;
+
+ for (i = 0; i < BATTERY_HISTORY_DAY_MAX; i++) {
+ if (heart_battery_get_usage_week_stime(i))
+ count++;
+ if (count > 1)
+ return 1;
+ }
+ return 0;
+}
+
+inline void heart_battery_set_usage_week(int day, int status, long sec_per_cap, long cap_counter)
+{
+ batt_stat.week_day_usage[day].sec_per_cap[status] = sec_per_cap;
+ batt_stat.week_day_usage[day].cap_counter[status] = cap_counter;
+}
+
+inline void heart_battery_set_prediction(int strategy, int status, long sec_per_cap, long cap_counter, long pred_min)
+{
+ batt_stat.prediction[status].sec_per_cap[strategy] = sec_per_cap;
+ batt_stat.prediction[status].cap_counter[strategy] = cap_counter;
+ batt_stat.prediction[status].time_pred_min[strategy] = pred_min;
+}
+
+inline long heart_battery_get_prediction_time(int strategy, int status)
+{
+ return batt_stat.prediction[status].time_pred_min[strategy];
+}
+
+inline time_t heart_battery_get_file_commit_timestamp()
+{
+ return last_file_commit_time;
+}
+
+inline void heart_battery_set_file_commit_timestamp(time_t timestamp)
+{
+ last_file_commit_time = timestamp;
+}
+
+static int heart_battery_save_used_time(char *key, struct battery_used *used)
+{
+ if (!key || !used)
+ return RESOURCED_ERROR_FAIL;
+
+ logging_leveldb_putv(key, strlen(key), "%d %d ",
+ used->used_time_sec, used->last_update_time);
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_save_status(char *key, struct battery_status *status)
+{
+ if (!key || !status)
+ return RESOURCED_ERROR_FAIL;
+
+ logging_leveldb_putv(key, strlen(key), "%d %ld %ld %ld %ld %d %d ",
+ status->curr_capacity,
+ status->curr_run_time_sec[DISCHARGING],
+ status->curr_cap_counter[DISCHARGING],
+ status->curr_run_time_sec[CHARGING],
+ status->curr_cap_counter[CHARGING],
+ status->curr_charger_status,
+ status->reset_mark);
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_save_usage(char *key, struct battery_usage *usage, int total_size)
+{
+ int i, len, num;
+ char buf[BATTERY_DATA_MAX] = {0, };
+
+ if (!key || !usage)
+ return RESOURCED_ERROR_FAIL;
+ len = 0;
+ num = total_size/sizeof(struct battery_usage);
+ for (i = 0; i < num; i++) {
+ len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%ld %ld %ld %ld %ld ",
+ usage[i].start_time,
+ usage[i].sec_per_cap[DISCHARGING],
+ usage[i].cap_counter[DISCHARGING],
+ usage[i].sec_per_cap[CHARGING],
+ usage[i].cap_counter[CHARGING]);
+ }
+ logging_leveldb_put(key, strlen(key), buf, len);
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_save_prediction(char *key, struct battery_prediction *prediction)
+{
+ int i, len;
+ char buf[BATTERY_DATA_MAX] = {0, };
+
+ if (!key || !prediction)
+ return RESOURCED_ERROR_FAIL;
+ len = 0;
+ for (i = 0; i < MAX_STRATEGY; i++) {
+ len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%ld %ld %ld %ld %ld %ld ",
+ prediction[DISCHARGING].sec_per_cap[i],
+ prediction[DISCHARGING].cap_counter[i],
+ prediction[DISCHARGING].time_pred_min[i],
+ prediction[CHARGING].sec_per_cap[i],
+ prediction[CHARGING].cap_counter[i],
+ prediction[CHARGING].time_pred_min[i]);
+ }
+ logging_leveldb_put(key, strlen(key), buf, len);
+ return RESOURCED_ERROR_NONE;
+};
+
+
+static int heart_battery_load_used_time(char *key, struct battery_used *used)
+{
+ int ret;
+ char *token;
+ char buf[BATTERY_DATA_MAX] = {0, };
+ char *saveptr;
+
+ if (!key || !used)
+ return RESOURCED_ERROR_FAIL;
+
+ ret = logging_leveldb_read(key, strlen(key), buf, sizeof(buf));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to read leveldb key: %s", key);
+ return RESOURCED_ERROR_FAIL;
+ }
+ token = strtok_r(buf, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ used->used_time_sec = atoi(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ used->last_update_time = atoi(token);
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_load_status(char *key, struct battery_status *status)
+{
+ int ret;
+ char *token;
+ char buf[BATTERY_DATA_MAX] = {0, };
+ char *saveptr;
+
+ if (!key || !status)
+ return RESOURCED_ERROR_FAIL;
+
+ ret = logging_leveldb_read(key, strlen(key), buf, sizeof(buf));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to read leveldb key: %s", key);
+ return RESOURCED_ERROR_FAIL;
+ }
+ token = strtok_r(buf, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_capacity = atoi(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_run_time_sec[DISCHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_cap_counter[DISCHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_run_time_sec[CHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_cap_counter[CHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->curr_charger_status = atoi(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ status->reset_mark = atoi(token);
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_load_usage(char *key, struct battery_usage *usage, int total_size)
+{
+ int i, num, ret;
+ char *token;
+ char buf[BATTERY_DATA_MAX] = {0, };
+ char *saveptr;
+
+ if (!key || !usage)
+ return RESOURCED_ERROR_FAIL;
+
+ ret = logging_leveldb_read(key, strlen(key), buf, sizeof(buf));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to read leveldb key: %s", key);
+ return RESOURCED_ERROR_FAIL;
+ }
+ i = 0;
+ num = total_size/sizeof(struct battery_usage);
+
+ token = strtok_r(buf, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ while (token && i++ < num) {
+ usage[i].start_time = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ usage[i].sec_per_cap[DISCHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ usage[i].cap_counter[DISCHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ usage[i].sec_per_cap[CHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ usage[i].cap_counter[CHARGING] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ _D("load [%d] stime: %ld, spc: %ld, count: %ld, spc: %ld, count: %ld",
+ i, usage[i].start_time, usage[i].sec_per_cap[DISCHARGING],
+ usage[i].cap_counter[DISCHARGING], usage[i].sec_per_cap[CHARGING],
+ usage[i].cap_counter[CHARGING]);
+ }
+ return RESOURCED_ERROR_NONE;
+};
+
+static int heart_battery_load_prediction(char *key, struct battery_prediction *prediction)
+{
+ int ret, i;
+ char *token;
+ char buf[BATTERY_DATA_MAX] = {0, };
+ char *saveptr;
+
+ if (!key || !prediction)
+ return RESOURCED_ERROR_FAIL;
+
+ ret = logging_leveldb_read(key, strlen(key), buf, sizeof(buf));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to read leveldb key: %s", key);
+ return RESOURCED_ERROR_FAIL;
+ }
+ token = strtok_r(buf, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ for (i = 0; i < MAX_STRATEGY && token; i++) {
+ prediction[DISCHARGING].sec_per_cap[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ prediction[DISCHARGING].cap_counter[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ prediction[DISCHARGING].time_pred_min[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ prediction[CHARGING].sec_per_cap[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ prediction[CHARGING].cap_counter[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ if (!token) {
+ _E("Failed to token value");
+ return RESOURCED_ERROR_FAIL;
+ }
+ prediction[CHARGING].time_pred_min[i] = atol(token);
+ token = strtok_r(NULL, " ", &saveptr);
+ }
+ return RESOURCED_ERROR_NONE;
+};
+
+static void heart_battery_update_used_time(time_t now, int status)
+{
+ if (batt_used.last_charger_status == DISCHARGING)
+ batt_used.used_time_sec +=
+ now - batt_used.last_update_time;
+ batt_used.last_charger_status = status;
+ batt_used.last_update_time = now;
+ heart_battery_save_used_time(BATTERY_USED_TIME, &batt_used);
+}
+
+static int heart_battery_get_capacity_history_size(void)
+{
+ int size, ret;
+
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return size;
+}
+
+static void heart_battery_insert_capacity(GSList **history_list, int capacity,
+ int diff_capacity, time_t timestamp, long used_time, long charging_time,
+ int charger_status, int reset_mark, int clear)
+{
+ static int old_reset_mark = 0;
+ GSList *iter, *next;
+ int ret, count;
+ struct heart_battery_capacity *lbc, *tlbc;
+
+ lbc = malloc(sizeof(struct heart_battery_capacity));
+ if (!lbc) {
+ _E("malloc failed");
+ return;
+ }
+ lbc->capacity = capacity;
+ lbc->diff_capacity = diff_capacity;
+ lbc->used_time = used_time;
+ lbc->charging_time = charging_time;
+ lbc->charger_status = charger_status;
+ lbc->reset_mark = reset_mark;
+ lbc->timestamp = timestamp;
+
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ free(lbc);
+ return;
+ }
+ /* clean all history when reset event */
+ if (clear && *history_list && lbc->reset_mark != old_reset_mark) {
+ g_slist_free_full(*history_list, free);
+ *history_list = NULL;
+ }
+
+ /* history reached maximum limitation number */
+ if (*history_list && g_slist_length(*history_list) > BATTERY_CAPACITY_MAX) {
+ count = 0;
+ gslist_for_each_safe(*history_list, iter, next, tlbc) {
+ *history_list = g_slist_remove(*history_list, (gpointer)tlbc);
+ free(tlbc);
+ if (BATTERY_CLEAN_MAX < count++)
+ break;
+ }
+ }
+ old_reset_mark = lbc->reset_mark;
+ *history_list = g_slist_append(*history_list, (gpointer)lbc);
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ }
+}
+
+/* ======================== Serialization/Deserialization ==================== */
+
+static int heart_battery_status_save_to_db(void)
+{
+ heart_battery_save_used_time(BATTERY_USED_TIME, &batt_used);
+ heart_battery_save_status(BATTERY_STATUS, &batt_stat);
+
+ heart_battery_save_usage(BATTERY_RESET_USAGE, batt_stat.batt_reset_usage, sizeof(batt_stat.batt_reset_usage));
+ heart_battery_save_usage(BATTERY_WEEK_DAY_USAGE, batt_stat.week_day_usage, sizeof(batt_stat.week_day_usage));
+ heart_battery_save_usage(BATTERY_LEVEL_USAGE, batt_stat.batt_lvl_usage, sizeof(batt_stat.batt_lvl_usage));
+
+ heart_battery_save_prediction(BATTERY_PREDICTION, batt_stat.prediction);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_battery_status_read_from_db(void)
+{
+ heart_battery_load_used_time(BATTERY_USED_TIME, &batt_used);
+ heart_battery_load_status(BATTERY_STATUS, &batt_stat);
+
+ heart_battery_load_usage(BATTERY_RESET_USAGE, batt_stat.batt_reset_usage, sizeof(batt_stat.batt_reset_usage));
+ heart_battery_load_usage(BATTERY_WEEK_DAY_USAGE, batt_stat.week_day_usage, sizeof(batt_stat.week_day_usage));
+ heart_battery_load_usage(BATTERY_LEVEL_USAGE, batt_stat.batt_lvl_usage, sizeof(batt_stat.batt_lvl_usage));
+
+ heart_battery_load_prediction(BATTERY_PREDICTION, batt_stat.prediction);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_battery_capacity_save_to_file(char *filename)
+{
+ int size, ret, count, len = 0;
+ struct heart_battery_capacity *lbc;
+ GSList *iter, *next;
+ FILE *fp;
+ char buf[BATTERY_DATA_MAX] = {0, };
+
+ if (!capacity_history_list) {
+ _E("capacity history is NULL!");
+ return RESOURCED_ERROR_NONE;
+ }
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+ fp = fopen(filename, "w");
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+ gslist_for_each_item(iter, capacity_history_list) {
+ lbc = (struct heart_battery_capacity *)iter->data;
+ if (!lbc)
+ break;
+ len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%d %d %ld %ld %ld %d %d\n",
+ lbc->capacity, lbc->diff_capacity, lbc->timestamp, lbc->used_time,
+ lbc->charging_time, lbc->charger_status,
+ lbc->reset_mark);
+ if (BATTERY_DATA_MAX < len + BATTERY_LINE_MAX) {
+ fputs(buf, fp);
+ len = 0;
+ }
+ }
+ fputs(buf, fp);
+ fclose(fp);
+ if (BATTERY_CAPACITY_MAX < size) {
+ count = 0;
+ gslist_for_each_safe(capacity_history_list, iter, next, lbc) {
+ capacity_history_list = g_slist_remove(capacity_history_list, (gpointer)lbc);
+ free(lbc);
+ if (BATTERY_CLEAN_MAX < count++)
+ break;
+ }
+ }
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_battery_capacity_read_from_file(char *filename)
+{
+ int len;
+ int capacity, diff_capacity, charger_status, reset_mark;
+ long used_time, charging_time;
+ time_t timestamp;
+ FILE *fp;
+ char buf[BATTERY_DATA_MAX] = {0, };
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+ while (fgets(buf, BATTERY_DATA_MAX, fp)) {
+ len = sscanf(buf, "%d %d %ld %ld %ld %d %d", &capacity, &diff_capacity,
+ ×tamp, &used_time, &charging_time,
+ &charger_status, &reset_mark);
+ if (len < 0) {
+ _E("sscanf failed");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ heart_battery_insert_capacity(&capacity_history_list, capacity, diff_capacity,
+ timestamp, used_time, charging_time,
+ charger_status, reset_mark, true);
+ }
+ fclose(fp);
+ return RESOURCED_ERROR_NONE;
+}
+
+/* ==================== Serialization/Deserialization END ==================== */
+
+static void heart_battery_save_to_file(bool force)
+{
+ int ret;
+ time_t now = logging_get_time(CLOCK_BOOTTIME);
+
+ heart_battery_update_used_time(now, batt_stat.curr_charger_status);
+
+ if (!force &&
+ heart_battery_get_file_commit_timestamp() + HEART_BATTERY_SAVE_INTERVAL >= now)
+ return;
+
+ ret = heart_battery_status_save_to_db();
+ if (ret) {
+ _E("failed to save status db");
+ }
+
+ ret = heart_battery_capacity_save_to_file(HEART_BATTERY_CAPACITY_DATA_FILE);
+ if (ret) {
+ _E("failed to save capacity file");
+ }
+ heart_battery_set_file_commit_timestamp(now);
+}
+
+void heart_battery_update(struct logging_table_form *data, void *user_data)
+{
+ heart_battery_save_to_file(false);
+}
+
+static int heart_battery_get_level_usage_index(int capacity)
+{
+ return (capacity > 49) ? BATTERY_LEVEL_HIGH :
+ (capacity < 16) ? BATTERY_LEVEL_LOW : BATTERY_LEVEL_MID;
+}
+
+static int heart_battery_get_week_day_usage_index(time_t timestamp)
+{
+ int i;
+
+ for (i = 0; i < BATTERY_HISTORY_DAY_MAX; i++) {
+ if (!heart_battery_get_usage_week_stime(i))
+ return i;
+ else if (abs(timestamp - heart_battery_get_usage_week_stime(i)) < DAY_TO_SEC(1))
+ return i;
+ }
+ for (i = 0; i < BATTERY_HISTORY_DAY_MAX - 1; i++) {
+ batt_stat.week_day_usage[i].start_time =
+ batt_stat.week_day_usage[i + 1].start_time;
+ batt_stat.week_day_usage[i].sec_per_cap[DISCHARGING] =
+ batt_stat.week_day_usage[i + 1].sec_per_cap[DISCHARGING];
+ batt_stat.week_day_usage[i].sec_per_cap[CHARGING] =
+ batt_stat.week_day_usage[i + 1].sec_per_cap[CHARGING];
+ batt_stat.week_day_usage[i].cap_counter[DISCHARGING] =
+ batt_stat.week_day_usage[i + 1].cap_counter[DISCHARGING];
+ batt_stat.week_day_usage[i].cap_counter[CHARGING] =
+ batt_stat.week_day_usage[i + 1].cap_counter[CHARGING];
+ }
+ return BATTERY_HISTORY_DAY_MAX - 1;
+}
+
+static int heart_battery_get_batt_reset_usage_index(void)
+{
+ int i;
+
+ for(i = 0; i < BATTERY_HISTORY_RESET_MAX; i++) {
+ if (heart_battery_get_usage_reset_count(i, DISCHARGING) < BATTERY_HISTORY_COUNT_MAX
+ && heart_battery_get_usage_reset_count(i, CHARGING) < BATTERY_HISTORY_COUNT_MAX)
+ return i;
+ }
+ for (i = 0; i < BATTERY_HISTORY_RESET_MAX - 1; i++) {
+ batt_stat.batt_reset_usage[i].start_time =
+ batt_stat.batt_reset_usage[i + 1].start_time;
+ batt_stat.batt_reset_usage[i].sec_per_cap[DISCHARGING] =
+ batt_stat.batt_reset_usage[i + 1].sec_per_cap[DISCHARGING];
+ batt_stat.batt_reset_usage[i].sec_per_cap[CHARGING] =
+ batt_stat.batt_reset_usage[i + 1].sec_per_cap[CHARGING];
+ batt_stat.batt_reset_usage[i].cap_counter[DISCHARGING] =
+ batt_stat.batt_reset_usage[i + 1].cap_counter[DISCHARGING];
+ batt_stat.batt_reset_usage[i].cap_counter[CHARGING] =
+ batt_stat.batt_reset_usage[i + 1].cap_counter[CHARGING];
+ }
+ return BATTERY_HISTORY_RESET_CURRENT;
+}
+
+static int heart_battery_reset(void *data)
+{
+ int idx;
+ long total_time, total_count, sec_per_cap;
+
+ idx = heart_battery_get_batt_reset_usage_index();
+
+ /* DISCHARGING */
+ total_time = 0; total_count = 0;
+ total_time = heart_battery_get_usage_reset_total_time(idx, DISCHARGING) + batt_stat.curr_run_time_sec[DISCHARGING];
+ total_count = heart_battery_get_usage_reset_count(idx, DISCHARGING) + batt_stat.curr_cap_counter[DISCHARGING];
+
+ if (total_time && total_count) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[DISCHARGING][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[DISCHARGING][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[DISCHARGING][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[DISCHARGING][DEFAULT_MAX];
+ heart_battery_set_usage_reset(idx, DISCHARGING, sec_per_cap, total_count);
+ }
+ /* CHARGING */
+ total_time = 0; total_count = 0;
+ total_time = heart_battery_get_usage_reset_total_time(idx, CHARGING)
+ + batt_stat.curr_run_time_sec[CHARGING];
+ total_count = heart_battery_get_usage_reset_count(idx, CHARGING) + batt_stat.curr_cap_counter[CHARGING];
+
+ if (total_time && total_count) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[CHARGING][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[CHARGING][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[CHARGING][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[CHARGING][DEFAULT_MAX];
+ heart_battery_set_usage_reset(idx, CHARGING, sec_per_cap, total_count);
+ }
+
+ batt_stat.reset_mark = batt_stat.reset_mark ? 0 : 1; /* Swap reset_mark */
+ batt_stat.reset_mark_timestamp = time(NULL);
+ batt_stat.curr_run_time_sec[DISCHARGING] = 0;
+ batt_stat.curr_run_time_sec[CHARGING] = 0;
+ batt_stat.curr_cap_counter[DISCHARGING] = 0;
+ batt_stat.curr_cap_counter[CHARGING] = 0;
+ batt_used.used_time_sec = 0;
+ batt_used.last_update_time = logging_get_time(CLOCK_BOOTTIME);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static long heart_battery_compute_remaining_time_in_min(int capacity_count, long sec_per_cap)
+{
+ /*
+ * Calculates and returns remaining time in minutes based on number
+ * of capacity changes and time needed for one change.
+ */
+ long time;
+
+ time = (capacity_count * sec_per_cap); /* seconds */
+ time = time + 30; /* add 30s margin */
+ time = time / 60; /* change to minutes */
+ return time;
+}
+
+static void heart_battery_calculate_prediction(enum charging_goal goal)
+{
+ int i, capacity, level;
+ long total_time, total_count, sec_per_cap, pred_min;
+ long low_count, mid_count, high_count;
+ struct heart_battery_capacity *lbc = NULL;
+ GArray *arrays = NULL;
+
+ if (goal == CHARGING) {
+ capacity = REMAIN_CAPACITY(batt_stat.curr_capacity);
+ } else {
+ capacity = batt_stat.curr_capacity;
+ }
+
+
+ /* PREDICTION METHOD: total average */
+ total_time = 0;
+ total_count = 0;
+ for (i = 0; i < BATTERY_HISTORY_RESET_MAX; i++) {
+ total_time += heart_battery_get_usage_reset_total_time(i, goal);
+ total_count += heart_battery_get_usage_reset_count(i, goal);
+ }
+ total_time += batt_stat.curr_run_time_sec[goal];
+ total_count += batt_stat.curr_cap_counter[goal];
+
+ if (total_time && total_count >= BATTERY_PREDICTION_DATA_MIN) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[goal][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[goal][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MAX];
+ pred_min = heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap);
+ heart_battery_set_prediction(TA, goal,
+ sec_per_cap, total_count,
+ pred_min);
+ } else {
+ heart_battery_set_prediction(TA, goal, 0, 0, 0);
+ }
+
+
+ /* PREDICTION METHOD:
+ * Prediction of battery remaining usage time
+ * considering users' psychological usage patterns
+ * by batt_lvl_usage of battery charge
+ * */
+ pred_min = 0;
+ sec_per_cap = 0;
+ level = heart_battery_get_level_usage_index(capacity);
+ low_count = heart_battery_get_usage_level_count(BATTERY_LEVEL_LOW, goal);
+ mid_count = heart_battery_get_usage_level_count(BATTERY_LEVEL_MID, goal);
+ high_count = heart_battery_get_usage_level_count(BATTERY_LEVEL_HIGH, goal);
+
+ if (level == BATTERY_LEVEL_LOW && low_count) {
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_LOW, goal);
+ pred_min = heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap);
+ } else if (level == BATTERY_LEVEL_MID && low_count && mid_count) {
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_LOW, goal);
+ pred_min = heart_battery_compute_remaining_time_in_min(15, sec_per_cap);
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_MID, goal);
+ pred_min +=
+ heart_battery_compute_remaining_time_in_min(capacity - 15, sec_per_cap);
+ } else if (level == BATTERY_LEVEL_HIGH && low_count && mid_count && high_count) {
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_LOW, goal);
+ pred_min = heart_battery_compute_remaining_time_in_min(15, sec_per_cap);
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_MID, goal);
+ pred_min +=
+ heart_battery_compute_remaining_time_in_min(35, sec_per_cap);
+ sec_per_cap = heart_battery_get_usage_level_spc(BATTERY_LEVEL_HIGH, goal);
+ pred_min +=
+ heart_battery_compute_remaining_time_in_min(capacity - 50, sec_per_cap);
+ }
+ heart_battery_set_prediction(PCB, goal, 0, 0, pred_min);
+
+
+ /* PREDICTION METHOD: week average */
+ total_time = 0;
+ total_count = 0;
+ for (i = 0; i < BATTERY_HISTORY_DAY_MAX; i++) {
+ total_time += heart_battery_get_usage_week_total_time(i, goal);
+ total_count += heart_battery_get_usage_week_count(i, goal);
+ }
+ if (total_time && total_count >= BATTERY_PREDICTION_DATA_MIN) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[goal][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[goal][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MAX];
+ pred_min =
+ heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap);
+ heart_battery_set_prediction(WEEK, goal, sec_per_cap, total_count, pred_min);
+ } else
+ heart_battery_set_prediction(WEEK, goal, 0, 0, 0);
+
+
+ /* PREDICTION METHOD: last BATTERY_PREDICTION_COUNT data average */
+ arrays = g_array_new(FALSE, FALSE, sizeof(struct heart_battery_capacity *));
+ if (!arrays) {
+ _E("Failed to alloc array");
+ return;
+ }
+ if (heart_battery_get_capacity_history_latest(arrays, goal, BATTERY_PREDICTION_LATEST_COUNT) != RESOURCED_ERROR_NONE) {
+ _E("Failed to get battery capacity history");
+ return;
+ }
+ if (!arrays->len) {
+ _E("No battery capacity history data");
+ }
+ total_time = 0;
+ total_count = 0;
+ for (i = 0; i < arrays->len; i++) {
+ lbc = g_array_index(arrays, struct heart_battery_capacity *, i);
+ if (!lbc)
+ break;
+ total_count += lbc->diff_capacity;
+ if (goal == CHARGING)
+ total_time += lbc->charging_time;
+ else
+ total_time += lbc->used_time;
+ }
+ if (total_time && total_count >= BATTERY_PREDICTION_DATA_MIN) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[goal][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[goal][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MAX];
+
+ pred_min =
+ heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap);
+ heart_battery_set_prediction(COUNT, goal, sec_per_cap, total_count, pred_min);
+ } else
+ heart_battery_set_prediction(COUNT, goal, 0, 0, 0);
+ g_array_free(arrays, TRUE);
+ arrays = NULL;
+
+
+ /* PREDICTION METHOD: last BATTERY_PREDICTION_PERIOD hours average */
+ arrays = g_array_new(FALSE, FALSE, sizeof(struct heart_battery_capacity *));
+ if (!arrays) {
+ _E("Failed to alloc array");
+ return;
+ }
+ if (heart_battery_get_capacity_history(arrays, BATTERY_PREDICTION_PERIOD) != RESOURCED_ERROR_NONE) {
+ _E("Failed to get battery capacity history");
+ return;
+ }
+ if (!arrays->len) {
+ _E("No battery capacity history data");
+ }
+ total_time = 0;
+ total_count = 0;
+ for (i = 0; i < arrays->len; i++) {
+ lbc = g_array_index(arrays, struct heart_battery_capacity *, i);
+ if (!lbc)
+ break;
+ if (goal == CHARGING) {
+ if (lbc->charger_status != CHARGING)
+ continue;
+ total_time += lbc->charging_time;
+ total_count += lbc->diff_capacity;
+ } else {
+ if (lbc->charger_status != DISCHARGING)
+ continue;
+ total_time += lbc->used_time;
+ total_count += lbc->diff_capacity;
+ }
+ }
+ g_array_free(arrays, TRUE);
+ arrays = NULL;
+ if (total_time && total_count >= BATTERY_PREDICTION_DATA_MIN) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap < default_sec_per_cap[goal][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[goal][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[goal][DEFAULT_MAX];
+ pred_min =
+ heart_battery_compute_remaining_time_in_min(capacity, sec_per_cap);
+ heart_battery_set_prediction(PERIOD, goal, sec_per_cap, total_count, pred_min);
+
+ } else
+ heart_battery_set_prediction(PERIOD, goal, 0, 0, 0);
+
+ /* Log values of all predictions calculated */
+ for (i = 0; i < MAX_STRATEGY; i++) {
+ _I("%s %d %ld %ld %ld",
+ (goal == DISCHARGING) ? "TimeToEmpty:" : "TimeToFull:",
+ batt_stat.curr_capacity,
+ batt_stat.prediction[goal].sec_per_cap[i],
+ batt_stat.prediction[goal].cap_counter[i],
+ batt_stat.prediction[goal].time_pred_min[i]);
+ }
+}
+
+static int heart_battery_add_capacity(int capacity)
+{
+ char info[BATTERY_DATA_MAX];
+ int ret, idx, status;
+ long time_diff_capacity_lvl[MAX_CHARGER_STATE];
+ int diff_capacity_lvl;
+ long total_time, total_count, sec_per_cap;
+ time_t timestamp = time(NULL);
+ time_t curr_wall_time = logging_get_time(CLOCK_BOOTTIME);
+
+ status = batt_stat.curr_charger_status;
+ /* calculate diff */
+ time_diff_capacity_lvl[status] = curr_wall_time - batt_stat.last_event_wall_time;
+
+ if (time_diff_capacity_lvl[status] < 0) {
+ batt_stat.last_event_wall_time = curr_wall_time;
+ return 0;
+ }
+
+ time_diff_capacity_lvl[!status] = 0;
+
+ if (!batt_stat.curr_capacity)
+ diff_capacity_lvl = 1;
+ else
+ diff_capacity_lvl = abs(batt_stat.curr_capacity - capacity);
+
+ _I("%d -> %d %ld %ld", batt_stat.curr_capacity, capacity,
+ timestamp, time_diff_capacity_lvl[status]);
+
+ /* update battery current status */
+ batt_stat.last_event_wall_time = curr_wall_time;
+ batt_stat.curr_capacity = capacity;
+
+ /* Full Charging status */
+ if (status == CHARGING && !REMAIN_CAPACITY(capacity) && !diff_capacity_lvl)
+ return 0;
+
+ /* update run usage */
+ batt_stat.curr_run_time_sec[status] += time_diff_capacity_lvl[status];
+ batt_stat.curr_cap_counter[status] += diff_capacity_lvl;
+
+ /* update batt_lvl_usage usage */
+ total_time = 0;
+ total_count = 0;
+
+ if (status == CHARGING)
+ idx = heart_battery_get_level_usage_index(REMAIN_CAPACITY(capacity));
+ else
+ idx = heart_battery_get_level_usage_index(capacity);
+
+ total_time = heart_battery_get_usage_level_total_time(idx, status) + time_diff_capacity_lvl[status];
+ if (total_time)
+ total_count = heart_battery_get_usage_level_count(idx, status) + diff_capacity_lvl;
+
+ if (total_count) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap == 0)
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_AVG];
+ else if (sec_per_cap < default_sec_per_cap[status][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[status][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_MAX];
+ /*
+ * If counts reached MAXIMUM number,
+ * counts is divided by 2 to reduce previous data's effect to equation
+ */
+ if (total_count >= BATTERY_HISTORY_COUNT_MAX)
+ total_count = total_count >> 1;
+
+ heart_battery_set_usage_level(idx, status, sec_per_cap, total_count);
+ heart_battery_set_usage_level_stime(idx, timestamp);
+ }
+
+ /* update day usage */
+ total_time = 0;
+ total_count = 0;
+
+ idx = heart_battery_get_week_day_usage_index(timestamp);
+ total_time = heart_battery_get_usage_week_total_time(idx, status) + time_diff_capacity_lvl[status];
+ if (total_time)
+ total_count = heart_battery_get_usage_week_count(idx, status) + diff_capacity_lvl;
+
+ if (total_count) {
+ sec_per_cap = total_time / total_count;
+ if (sec_per_cap == 0)
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_AVG];
+ else if (sec_per_cap < default_sec_per_cap[status][DEFAULT_MIN])
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_MIN];
+ else if (sec_per_cap > default_sec_per_cap[status][DEFAULT_MAX])
+ sec_per_cap = default_sec_per_cap[status][DEFAULT_MAX];
+ heart_battery_set_usage_week(idx, status, sec_per_cap, total_count);
+ heart_battery_set_usage_week_stime(idx, CALCULATE_DAY_BASE_TIME(timestamp));
+ }
+
+ heart_battery_calculate_prediction(batt_stat.curr_charger_status);
+
+ /* db backup */
+ snprintf(info, sizeof(info), "%d %d %ld %ld %d %d ",
+ capacity, diff_capacity_lvl,
+ time_diff_capacity_lvl[DISCHARGING], time_diff_capacity_lvl[CHARGING],
+ batt_stat.curr_charger_status, batt_stat.reset_mark);
+ ret = logging_write(BATTERY_NAME, TIZEN_SYSTEM_BATTERY_APPID,
+ TIZEN_SYSTEM_APPID, timestamp, info);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ /* insert capacity history list */
+ heart_battery_insert_capacity(&capacity_history_list, capacity, diff_capacity_lvl,
+ timestamp, time_diff_capacity_lvl[DISCHARGING],
+ time_diff_capacity_lvl[CHARGING], batt_stat.curr_charger_status,
+ batt_stat.reset_mark, true);
+
+ _D("battery_heart_capacity_write %d diff_capacity %ld, used time %ld, charging time %ld, charger status %d, reset_mark %d",
+ capacity, diff_capacity_lvl,
+ time_diff_capacity_lvl[DISCHARGING], time_diff_capacity_lvl[CHARGING],
+ batt_stat.curr_charger_status, batt_stat.reset_mark);
+
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+/* ============================ DBUS -> DEVICED on demand ==================== */
+
+static int heart_battery_get_capacity(void)
+{
+ int capacity, ret;
+ DBusMessage *msg;
+
+ msg = dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY,
+ GET_BATTERY_CAPACITY,
+ NULL, NULL);
+ if (!msg) {
+ _E("Failed to sync DBUS message.");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &capacity, DBUS_TYPE_INVALID);
+ dbus_message_unref(msg);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return RESOURCED_ERROR_FAIL;
+ }
+ return capacity;
+}
+
+static int heart_battery_get_charger_status(void)
+{
+ int status, ret;
+ DBusMessage *msg;
+
+ msg = dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY,
+ GET_CHARGER_STATUS,
+ NULL, NULL);
+ if (!msg) {
+ _E("Failed to sync DBUS message.");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &status, DBUS_TYPE_INVALID);
+ dbus_message_unref(msg);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (status > 0)
+ return CHARGING;
+ return DISCHARGING;
+}
+
+/* ========================= DBUS -> DEVICED on demand END ================= */
+
+/* ============================ DBUS -> DEVICED handler ====================== */
+static void heart_battery_capacity_status(void *data, DBusMessage *msg)
+{
+ /*
+ * This handler is called when battery capacity value change in 1%
+ *
+ * The message have current percent value of capacity
+ *
+ * (This requires deviced with commit at least:
+ * "f1ae1d1f270e9 battery: add battery capacity dbus signal broadcast")
+ */
+
+ int ret, capacity;
+
+ ret = dbus_message_is_signal(msg, DEVICED_INTERFACE_BATTERY, GET_BATTERY_CAPACITY);
+ if (!ret) {
+ _E("dbus_message_is_signal error");
+ return;
+ }
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &capacity, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return;
+ }
+ heart_battery_add_capacity(capacity);
+ heart_battery_update_used_time(logging_get_time(CLOCK_BOOTTIME),
+ batt_stat.curr_charger_status);
+}
+
+static void heart_battery_charger_status(void *data, DBusMessage *msg)
+{
+ /*
+ * This handler is called when USB cable with charging capabilities
+ * is connected or disconnected from the device.
+ *
+ * The message have current status of charger connection.
+ * STATUSES:
+ * 0 - charger was disconnected
+ * 1 - charger was connected
+ */
+ int ret, charger_status, cap_history_size;
+
+ ret = dbus_message_is_signal(msg, DEVICED_INTERFACE_BATTERY, GET_CHARGER_STATUS);
+ if (!ret) {
+ _E("dbus_message_is_signal error");
+ return;
+ }
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &charger_status, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return;
+ }
+
+ /* Update the statistics with capacity when charger state was changed */
+ heart_battery_add_capacity(batt_stat.curr_capacity);
+
+ cap_history_size = heart_battery_get_capacity_history_size();
+
+ if (charger_status == DISCHARGING && batt_stat.curr_capacity >= 90) {
+ /*
+ * If battery is charged over 90 and charger was disconnected.
+ * So most probably the phone was "charged".
+ * Let's reset the statistics.
+ */
+ resourced_notify(RESOURCED_NOTIFIER_DATA_RESET, NULL);
+ } else if (charger_status == DISCHARGING && cap_history_size >= BATTERY_CAPACITY_MAX) {
+ /*
+ * Charger is not connected and the battery history is over limit.
+ * Let's reset the statistics.
+ */
+ resourced_notify(RESOURCED_NOTIFIER_DATA_RESET, NULL);
+ }
+ /* Update current charger connection status */
+ batt_stat.curr_charger_status = charger_status;
+ heart_battery_update_used_time(logging_get_time(CLOCK_BOOTTIME),
+ batt_stat.curr_charger_status);
+ heart_battery_calculate_prediction(batt_stat.curr_charger_status);
+}
+
+/* ========================= DBUS -> DEVICED handler END ==================== */
+
+int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int max_size)
+{
+ int ret, size, count;
+ struct heart_battery_capacity *lbc, *lbci;
+ GSList *iter, *rlist;
+
+ if (!capacity_history_list) {
+ _E("empty capacity history list");
+ return RESOURCED_ERROR_FAIL;
+ }
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ return RESOURCED_ERROR_NONE;
+ }
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ count = 0;
+
+ rlist = g_slist_copy(capacity_history_list);
+
+ rlist = g_slist_reverse(rlist);
+
+ gslist_for_each_item(iter, rlist) {
+ lbc = (struct heart_battery_capacity *)iter->data;
+ if (!lbc)
+ break;
+ if (charge < MAX_CHARGER_STATE && charge != lbc->charger_status)
+ continue;
+ count++;
+ if (max_size < count)
+ break;
+ lbci = malloc(sizeof(struct heart_battery_capacity));
+ if (!lbci) {
+ _E("malloc failed");
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ lbci->capacity = lbc->capacity;
+ lbci->diff_capacity = lbc->diff_capacity;
+ if (!lbc->diff_capacity)
+ count--;
+ lbci->used_time = lbc->used_time;
+ lbci->charging_time = lbc->charging_time;
+ lbci->charger_status = lbc->charger_status;
+ g_array_prepend_val(arrays, lbci);
+ }
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+int heart_battery_get_capacity_history(GArray *arrays, enum heart_data_period period)
+{
+ int ret, index, size;
+ struct heart_battery_capacity *lbc, *lbci;
+ GSList *iter;
+ time_t curr = time(NULL);
+
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!capacity_history_list) {
+ _E("empty capacity history list");
+ return RESOURCED_ERROR_FAIL;
+ }
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ return RESOURCED_ERROR_NONE;
+ }
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ gslist_for_each_item(iter, capacity_history_list) {
+ lbc = (struct heart_battery_capacity *)iter->data;
+ if (!lbc)
+ break;
+ if (index && (lbc->timestamp < curr - (index * 3600)))
+ continue;
+ lbci = malloc(sizeof(struct heart_battery_capacity));
+ if (!lbci) {
+ _E("malloc failed");
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ lbci->capacity = lbc->capacity;
+ lbci->diff_capacity = lbc->diff_capacity;
+ lbci->used_time = lbc->used_time;
+ lbci->charging_time = lbc->charging_time;
+ lbci->charger_status = lbc->charger_status;
+ g_array_append_val(arrays, lbci);
+ }
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+/* ============================ DBUS interface ====================== */
+
+static DBusMessage *edbus_get_battery_capacity_history_latest(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int i, ret, size, charge, max_size;
+ DBusMessage *reply;
+ DBusMessageIter d_iter;
+ DBusMessageIter arr;
+ GArray *arrays = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &charge, DBUS_TYPE_INT32, &max_size, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ return reply;
+ }
+ dbus_message_iter_init_append(reply, &d_iter);
+ arrays = g_array_new(FALSE, FALSE, sizeof(struct heart_battery_capacity *));
+ if (!arrays) {
+ _E("Failed to alloc array");
+ return reply;
+ }
+ if (heart_battery_get_capacity_history_latest(arrays, charge, max_size) != RESOURCED_ERROR_NONE) {
+ _E("Failed to get capacity history latest");
+ goto exit;
+ }
+ if (!arrays->len) {
+ _E("No battery capacity history data");
+ goto exit;
+ }
+ dbus_message_iter_open_container(&d_iter, DBUS_TYPE_ARRAY, "(iii)", &arr);
+ for (i = 0; i < arrays->len; i++) {
+ DBusMessageIter sub;
+ struct heart_battery_capacity *lbc;
+ lbc = g_array_index(arrays, struct heart_battery_capacity *, i);
+ if (!lbc)
+ break;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->capacity);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->used_time);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->charging_time);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&d_iter, &arr);
+exit:
+ g_array_free(arrays, TRUE);
+ return reply;
+}
+
+static DBusMessage *edbus_get_battery_capacity_history(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret, size, period, index;
+ DBusMessage *reply;
+ DBusMessageIter d_iter;
+ DBusMessageIter arr;
+ struct heart_battery_capacity *lbc;
+ GSList *iter;
+ time_t curr = time(NULL);
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+ size = g_slist_length(capacity_history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ return reply;
+ }
+ dbus_message_iter_init_append(reply, &d_iter);
+ dbus_message_iter_open_container(&d_iter, DBUS_TYPE_ARRAY, "(iii)", &arr);
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ dbus_message_iter_close_container(&d_iter, &arr);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ gslist_for_each_item(iter, capacity_history_list) {
+ DBusMessageIter sub;
+ lbc = (struct heart_battery_capacity *)iter->data;
+ if (!lbc)
+ break;
+ if (index && (lbc->timestamp < curr - (index * 3600)))
+ continue;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->capacity);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->used_time);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &lbc->charging_time);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ dbus_message_iter_close_container(&d_iter, &arr);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ dbus_message_iter_close_container(&d_iter, &arr);
+ return reply;
+}
+
+static DBusMessage *edbus_get_battery_used_time(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ heart_battery_update_used_time(logging_get_time(CLOCK_BOOTTIME),
+ batt_stat.curr_charger_status);
+ ret = batt_used.used_time_sec;
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+
+ return reply;
+}
+
+static int get_battery_remaining_time(int mode, int status)
+{
+ int i, ret, count;
+ long sum, time, cumul_average, trend_average;
+ double result;
+
+ ret = count = 0;
+ sum = time = 0;
+ cumul_average = trend_average = 0;
+ /* get prediction time of cumulative value */
+ for (i = 0; i <= WEEK; i++) {
+ time = heart_battery_get_prediction_time(i, status);
+ if (time) {
+ sum += time;
+ count++;
+ }
+ }
+ if (count)
+ cumul_average = sum / count;
+
+ count = 0;
+ sum = 0;
+ /* get prediction time of trend value */
+ for (i = COUNT; i < MAX_STRATEGY; i++) {
+ time = heart_battery_get_prediction_time(i, status);
+ if (time) {
+ sum += time;
+ count++;
+ }
+ }
+ if (count)
+ trend_average = sum / count;
+
+ /* failed to get prediction so return learning mode */
+ if (!cumul_average && !trend_average) {
+ if (batt_stat.curr_capacity != 100 && batt_stat.curr_capacity != 0)
+ ret = BATTERY_USAGE_LEARNING;
+ } else if (cumul_average && !trend_average) {
+ /* failed to get prediction of trend average */
+ ret = cumul_average;
+ } else if (!cumul_average && trend_average) {
+ /* failed to get prediction of cumulative average */
+ ret = trend_average;
+ } else
+ ret = ((cumul_average * CUMUL_WEIGHT) + (trend_average * TREND_WEIGHT));
+
+ if (status == CHARGING)
+ return ret;
+
+ switch (mode) {
+ case ULTRA_SAVING_MODE:
+ /* Fall through */
+ case POWER_SAVING_MODE:
+ result = (double)ret * default_mode_factor[mode];
+ return (int)result;
+ case POWER_NORMAL_MODE:
+ /* Fall through */
+ default:
+ return ret;
+ }
+}
+
+static DBusMessage *edbus_get_battery_remaining_time(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ int ret, mode;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &mode, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (!battery_learning_mode)
+ battery_learning_mode = heart_battery_get_learning_mode();
+
+ if (!battery_learning_mode) {
+ _E("data is not enough to calculate prediction");
+ ret = BATTERY_USAGE_LEARNING;
+ } else
+ ret = get_battery_remaining_time(mode, DISCHARGING);
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ _I("Remaining_time %d (mode: %d)", ret, mode);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_battery_charging_time(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ int ret;
+
+ ret = get_battery_remaining_time(POWER_NORMAL_MODE, CHARGING);
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ _I("Remaining_charging_time %d", ret);
+
+ return reply;
+}
+
+static DBusMessage *edbus_battery_save_to_file(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ ret = heart_battery_status_save_to_db();
+ if (ret) {
+ _E("save to db failed");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ ret = heart_battery_capacity_save_to_file(HEART_BATTERY_CAPACITY_DATA_FILE);
+ if (ret) {
+ _E("save to file failed");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { "GetBatteryCapacityHistory", "i", "a(iii)", edbus_get_battery_capacity_history },
+ { "GetBatteryCapacityHistoryLatest", "ii", "a(iii)", edbus_get_battery_capacity_history_latest },
+ { "GetBatteryUsedTime", NULL, "i", edbus_get_battery_used_time },
+ { "GetBatteryRemainingTime", "i", "i", edbus_get_battery_remaining_time },
+ { "GetBatteryChargingTime", NULL, "i", edbus_get_battery_charging_time },
+ { "SaveBatteryData", NULL, "i", edbus_battery_save_to_file },
+};
+
+/* ========================= DBUS interface END ==================== */
+static void heart_battery_used_time_init(int status)
+{
+ batt_used.last_charger_status = status;
+ batt_used.last_update_time = logging_get_time(CLOCK_BOOTTIME);
+}
+
+static void heart_battery_status_init(void)
+{
+ int i, ret, status, capacity;
+
+ batt_stat.curr_capacity = 0;
+ batt_stat.curr_run_time_sec[DISCHARGING] = 0;
+ batt_stat.curr_run_time_sec[CHARGING] = 0;
+ batt_stat.curr_cap_counter[DISCHARGING] = 0;
+ batt_stat.curr_cap_counter[CHARGING] = 0;
+ batt_stat.curr_charger_status = 0;
+ batt_stat.reset_mark = 0;
+
+ for (i = 0; i < BATTERY_HISTORY_RESET_MAX; i++) {
+ heart_battery_set_usage_reset_stime(i, 0);
+ heart_battery_set_usage_reset(i, DISCHARGING, 0, 0);
+ heart_battery_set_usage_reset(i, CHARGING, 0, 0);
+ }
+
+
+ for (i = 0; i < BATTERY_LEVEL_MAX; i++) {
+ heart_battery_set_usage_level_stime(i, 0);
+ heart_battery_set_usage_level(i, DISCHARGING, default_sec_per_cap[DISCHARGING][DEFAULT_AVG], 0);
+ heart_battery_set_usage_level(i, CHARGING, default_sec_per_cap[CHARGING][DEFAULT_AVG], 0);
+ }
+
+ for (i = 0; i < BATTERY_HISTORY_DAY_MAX; i++) {
+ heart_battery_set_usage_week_stime(i, 0);
+ heart_battery_set_usage_week(i, DISCHARGING, 0, 0);
+ heart_battery_set_usage_week(i, CHARGING, 0, 0);
+ }
+
+ for (i = 0; i < MAX_STRATEGY; i++) {
+ heart_battery_set_prediction(i, DISCHARGING, 0, 0, 0);
+ heart_battery_set_prediction(i, CHARGING, 0, 0, 0);
+ }
+
+ ret = heart_battery_status_read_from_db();
+ if (ret < 0) {
+ _E("Failed to read battery status data");
+ }
+
+ battery_learning_mode = heart_battery_get_learning_mode();
+
+ ret = heart_battery_capacity_read_from_file(HEART_BATTERY_CAPACITY_DATA_FILE);
+ if (ret < 0) {
+ _E("Failed to read battery capacity data");
+ }
+
+ capacity = heart_battery_get_capacity();
+ if (capacity > 0) {
+ batt_stat.curr_capacity = capacity;
+ }
+ status = heart_battery_get_charger_status();
+ if (status >= 0) {
+ batt_stat.curr_charger_status = status;
+ }
+ heart_battery_used_time_init(batt_stat.curr_charger_status);
+ heart_battery_calculate_prediction(batt_stat.curr_charger_status);
+ batt_stat.last_event_wall_time = logging_get_time(CLOCK_BOOTTIME);
+}
+
+static int low_battery_handler(void *data)
+{
+ heart_battery_save_to_file(false);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_battery_config(struct parse_result *result, void *user_data)
+{
+ int val;
+
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, HEART_BATTERY_CONF_SECTION))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strcmp(result->name, "POWER_NORMAL_MODE")) {
+ val = atoi(result->value);
+ if (val > 0)
+ default_mode_spc[POWER_NORMAL_MODE] = val;
+ _D("POWER_NORMAL_MODE SPC: %d", val);
+ } else if (!strcmp(result->name, "POWER_SAVING_MODE")) {
+ val = atoi(result->value);
+ if (val > 0)
+ default_mode_spc[POWER_SAVING_MODE] = val;
+ _D("POWER_SAVING_MODE SPC: %d", val);
+ } else if (!strcmp(result->name, "ULTRA_SAVING_MODE")) {
+ val = atoi(result->value);
+ if (val > 0)
+ default_mode_spc[ULTRA_SAVING_MODE] = val;
+ _D("ULTRA_POWER_SAVING_MODE SPC: %d", val);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void heart_battery_mode_factor_init(void)
+{
+ double val;
+
+ val = default_mode_spc[POWER_SAVING_MODE]/default_mode_spc[POWER_NORMAL_MODE];
+
+ if (1.0 < val)
+ default_mode_factor[POWER_SAVING_MODE] = val;
+ _I("POWER_SAVING_MODE factor: %f", val);
+
+ val = default_mode_spc[ULTRA_SAVING_MODE]/default_mode_spc[POWER_NORMAL_MODE];
+
+ if (1.0 < val)
+ default_mode_factor[ULTRA_SAVING_MODE] = val;
+ _I("ULTRA_POWER_SAVING_MODE factor: %f", val);
+}
+
+static int heart_battery_init(void *data)
+{
+ int ret;
+
+ ret = logging_module_init(BATTERY_NAME, ONE_DAY, TEN_MINUTE,
+ heart_battery_update, HEART_BATTERY_UPDATE_INTERVAL);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("logging module init failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed",
+ RESOURCED_PATH_LOGGING);
+ }
+ ret = register_edbus_signal_handler(DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY, GET_BATTERY_CAPACITY,
+ heart_battery_capacity_status, NULL);
+ if (ret < 0) {
+ _E("Failed to add a capacity status signal handler");
+ }
+
+ ret = register_edbus_signal_handler(DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY, GET_CHARGER_STATUS,
+ heart_battery_charger_status, NULL);
+ if (ret < 0) {
+ _E("Failed to add a charger status signal handler");
+ }
+
+ config_parse(HEART_CONF_FILE_PATH, heart_battery_config, NULL);
+
+ heart_battery_mode_factor_init();
+
+ heart_battery_status_init();
+
+ register_notifier(RESOURCED_NOTIFIER_LOW_BATTERY, low_battery_handler);
+ register_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_battery_reset);
+
+ heart_battery_set_file_commit_timestamp(logging_get_time(CLOCK_BOOTTIME));
+ _D("heart battery init finished");
+ return RESOURCED_ERROR_NONE;
+}
+
+void heart_capacity_history_update(struct logging_table_form *data, void *user_data)
+{
+ int status, reset, capacity, diff;
+ unsigned long discharging = 0, charging = 0;
+ GSList **history_list = NULL;
+
+ if (user_data)
+ history_list = (GSList **)user_data;
+ else
+ history_list = &capacity_history_list;
+
+ _D("%s %s %d %s", data->appid, data->pkgid, data->time, data->data);
+ if (sscanf(data->data, "%d %d %ld %ld %d %d ",
+ &capacity, &diff,
+ &discharging, &charging,
+ &status, &reset) < 0) {
+ _E("sscanf failed");
+ return;
+ }
+ heart_battery_insert_capacity(history_list, capacity,
+ diff, data->time, discharging, charging, status,
+ reset, false);
+}
+
+static int heart_battery_dump(FILE *fp, int mode, void *data)
+{
+ struct heart_battery_capacity *lbc;
+ GSList *iter;
+ char buf[BATTERY_DATA_MAX] = {0, };
+ int ret, size, len = 0;
+ time_t starttime;
+ char timestr[80];
+ struct tm loc_tm;
+ GSList *history_list = NULL;
+
+ starttime = time(NULL);
+ starttime -= mode;
+ localtime_r(&starttime, &loc_tm);
+ /* print timestamp */
+ strftime(timestr, sizeof(timestr),
+ "%Y-%m-%d %H:%M:%S%z", &loc_tm);
+
+ logging_read_foreach(BATTERY_NAME, NULL, NULL, starttime, 0,
+ heart_capacity_history_update, &history_list);
+
+ if (!history_list) {
+ _E("capacity history is NULL!");
+ return RESOURCED_ERROR_NONE;
+ }
+ LOG_DUMP(fp, "[BATTERY CAPACITY HISTORY] since %s\n", timestr);
+ LOG_DUMP(fp, "capacity diff timestamp used_time charging_time charger_status, reset_mark\n");
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ size = g_slist_length(history_list);
+ if (!size) {
+ _I("capacity history is empty");
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+ gslist_for_each_item(iter, history_list) {
+ lbc = (struct heart_battery_capacity *)iter->data;
+ if (!lbc)
+ break;
+ len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%d %d %ld %ld %ld %d %d\n",
+ lbc->capacity, lbc->diff_capacity, lbc->timestamp, lbc->used_time,
+ lbc->charging_time, lbc->charger_status,
+ lbc->reset_mark);
+ if (BATTERY_DATA_MAX < len + BATTERY_LINE_MAX) {
+ LOG_DUMP(fp, "%s\n", buf);
+ len = 0;
+ }
+ }
+ LOG_DUMP(fp, "%s\n", buf);
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ fflush(fp);
+ if (history_list) {
+ g_slist_free_full(history_list, free);
+ history_list = NULL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_battery_exit(void *data)
+{
+ int ret;
+ GSList *iter, *next;
+ struct heart_battery_capacity *lbc;
+
+ heart_battery_save_to_file(true);
+ ret = pthread_mutex_lock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ }
+
+ gslist_for_each_safe(capacity_history_list, iter, next, lbc) {
+ capacity_history_list = g_slist_remove(capacity_history_list, lbc);
+ free(lbc);
+ }
+ capacity_history_list = NULL;
+
+ ret = pthread_mutex_unlock(&heart_battery_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ }
+
+ unregister_notifier(RESOURCED_NOTIFIER_LOW_BATTERY, low_battery_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_battery_reset);
+
+ logging_module_exit();
+
+ _D("heart battery exit");
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_battery_ops = {
+ .name = "BATTERY",
+ .init = heart_battery_init,
+ .dump = heart_battery_dump,
+ .exit = heart_battery_exit,
+};
+HEART_MODULE_REGISTER(&heart_battery_ops)
--- /dev/null
+/*
+ * 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 heart-cpu.c
+ *
+ * @desc heart cpu module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <math.h>
+
+#include "proc-common.h"
+#include "notifier.h"
+#include "resourced.h"
+#include "edbus-handler.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+
+#define PROC_PATH "/proc/%d"
+#define PROC_STAT_PATH "/proc/%d/stat"
+#define CPU_NAME "cpu"
+#define CPU_DATA_MAX 1024
+#define CPU_ARRAY_MAX 24
+#define HEART_CPU_SAVE_INTERVAL 3600
+#define HEART_CPU_DATA_FILE HEART_FILE_PATH"/.cpu.dat"
+
+enum {
+ SERVICE = 0,
+ FOREG = 1,
+ BACKG = 2
+};
+
+struct heart_cpu_info {
+ unsigned long utime;
+ unsigned long stime;
+ int state;
+ pid_t pid;
+};
+
+struct heart_cpu_table {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ unsigned long total_utime;
+ unsigned long total_stime;
+ unsigned long utime;
+ unsigned long stime;
+ int fg_count;
+ unsigned long fg_time;
+ unsigned long bg_time;
+ GSList *last_pid_info;
+ pid_t last_pid;
+ time_t last_renew_time;
+ GArray *cpu_info;
+};
+
+static GHashTable *heart_cpu_app_list;
+static pthread_mutex_t heart_cpu_mutex = PTHREAD_MUTEX_INITIALIZER;
+static time_t last_file_commit_time;
+
+static void heart_cpu_remove_last_pid_info_exited(struct heart_cpu_table *table)
+{
+ char proc_path[sizeof(PROC_PATH) + MAX_DEC_SIZE(int)];
+ GSList *iter, *next;
+ struct heart_cpu_info *ci = NULL;
+
+ if (!table || !table->last_pid_info)
+ return;
+
+ gslist_for_each_safe(table->last_pid_info, iter, next, ci) {
+ snprintf(proc_path, sizeof(proc_path), PROC_PATH, ci->pid);
+ if (!access(proc_path, F_OK))
+ continue;
+ table->last_pid_info = g_slist_remove(table->last_pid_info, ci);
+ free(ci);
+ }
+}
+
+static struct heart_cpu_info *find_pid_info(struct heart_cpu_table *table, pid_t pid)
+{
+ GSList *iter = NULL;
+ struct heart_cpu_info *ci = NULL;
+
+ if (!table || !table->last_pid_info)
+ return NULL;
+
+ gslist_for_each_item(iter, table->last_pid_info) {
+ ci = (struct heart_cpu_info *)iter->data;
+ if (ci && ci->pid == pid)
+ return ci;
+ }
+ return NULL;
+}
+
+static int heart_cpu_get_cpu_time(pid_t pid, unsigned long *utime,
+ unsigned long *stime)
+{
+ char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
+ FILE *fp;
+
+ assert(utime != NULL);
+ assert(stime != NULL);
+
+ snprintf(proc_path, sizeof(proc_path), PROC_STAT_PATH, pid);
+ fp = fopen(proc_path, "r");
+ if (fp == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (fscanf(fp, "%lu %lu", utime, stime) < 1) {
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_write_data(struct proc_status *ps, pid_t pid, int type)
+{
+ int ret;
+ unsigned long utime, stime;
+ char info[CPU_DATA_MAX];
+ char *appid, *pkgid;
+
+ ret = heart_cpu_get_cpu_time(pid, &utime, &stime);
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ snprintf(info, sizeof(info), "%lu %lu %d %d ", utime, stime, pid, type);
+
+ ret = proc_get_id_info(ps, &appid, &pkgid);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to proc_get_id_info");
+ return ret;
+ }
+ ret = logging_write(CPU_NAME, appid, pkgid, time(NULL), info);
+ _D("heart_cpu_write_data : pid = %d, appname = %s, pkgname = %s, type=%d",
+ pid, appid, pkgid, type);
+ return ret;
+}
+
+static int heart_cpu_service_launch(void *data)
+{
+ int ret;
+ struct proc_status *ps = (struct proc_status *)data;
+
+ ret = heart_cpu_write_data(ps, ps->pid, SERVICE);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write cpu info %d", ps->pid);
+ return ret;
+ }
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_foreground_state(void *data)
+{
+ int ret;
+ struct proc_status *ps = (struct proc_status *)data;
+
+ ret = heart_cpu_write_data(ps, ps->pid, FOREG);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write cpu info %d", ps->pid);
+ return ret;
+ }
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_background_state(void *data)
+{
+ int ret;
+ GSList *giter = NULL;
+ struct proc_status *ps = (struct proc_status *)data;
+
+ ret = heart_cpu_write_data(ps, ps->pid, BACKG);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write cpu info %d", ps->pid);
+ return ret;
+ }
+ if (!ps->pai->childs)
+ return RESOURCED_ERROR_NONE;
+ gslist_for_each_item(giter, ps->pai->childs) {
+ struct child_pid *child = (struct child_pid *)(giter->data);
+ if (child) {
+ ret = heart_cpu_write_data(ps, child->pid, BACKG);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write child cpu info %d", child->pid);
+ return ret;
+ }
+ }
+ }
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_update_state(void *data)
+{
+ int ret, state;
+ GSList *giter = NULL;
+ struct proc_status *ps = (struct proc_status *)data;
+
+ if (!ps->pai) {
+ _E("Invalid parameter");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ if (ps->pai->lru_state == PROC_FOREGROUND)
+ state = FOREG;
+ else
+ state = BACKG;
+
+ ret = heart_cpu_write_data(ps, ps->pid, state);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write cpu info %d", ps->pid);
+ return ret;
+ }
+ _D("heart_cpu_update_state : pid = %d, state = %d",
+ ps->pid, state);
+ if (!ps->pai->childs)
+ return RESOURCED_ERROR_NONE;
+ gslist_for_each_item(giter, ps->pai->childs) {
+ struct child_pid *child = (struct child_pid *)(giter->data);
+ if (child) {
+ ret = heart_cpu_write_data(ps, child->pid, state);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to write cpu info %d", child->pid);
+ return ret;
+ }
+ }
+ }
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_update_app_list(void *data)
+{
+ GSList *giter = NULL;
+ struct proc_app_info *pai = NULL;
+
+ gslist_for_each_item(giter, proc_app_list) {
+ struct proc_status ps;
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->ai)
+ continue;
+ ps.pid = pai->main_pid;
+ ps.appid = pai->appid;
+ ps.pai = pai;
+ heart_cpu_update_state(&ps);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void heart_free_value(gpointer value)
+{
+ struct heart_cpu_table *table = (struct heart_cpu_table *)value;
+
+ if (!table)
+ return;
+
+ if (table->last_pid_info)
+ g_slist_free_full(table->last_pid_info, free);
+
+ if (table->cpu_info)
+ g_array_free(table->cpu_info, TRUE);
+
+ free(table);
+}
+
+static int heart_cpu_read_length(char *buf, int count)
+{
+ int i, find = 0;
+ int len = strlen(buf);
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] == ' ')
+ find++;
+ if (find == count)
+ return i + 1;
+ }
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int heart_cpu_read_from_file(GHashTable *hashtable, char *filename)
+{
+ int i, len, ret, fg_count, state;
+ unsigned long total_utime, total_stime;
+ unsigned long utime, stime;
+ unsigned long fg_time, bg_time;
+ pid_t pid;
+ FILE *fp;
+ struct heart_cpu_table *table;
+ char appid[MAX_APPID_LENGTH] = {0, };
+ char pkgid[MAX_PKGNAME_LENGTH] = {0, };
+ char buf[CPU_DATA_MAX] = {0, };
+
+ fp = fopen(filename, "r");
+
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ while (fgets(buf, CPU_DATA_MAX, fp)) {
+ table = malloc(sizeof(struct heart_cpu_table));
+
+ if (!table) {
+ _E("malloc failed");
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* make return values */
+ ret = sscanf(buf, "%s %s %lu %lu %lu %lu %lu %lu %d ", appid, pkgid,
+ &total_utime, &total_stime,
+ &utime, &stime,
+ &fg_time, &bg_time, &fg_count);
+
+ if (ret <= 0) {
+ _E("sscanf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", appid) < 0) {
+ _E("sprintf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", pkgid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ len = heart_cpu_read_length(buf, 9);
+ if (len <= 0) {
+ _E("sscanf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ table->total_utime = total_utime;
+ table->total_stime = total_stime;
+ table->utime = utime;
+ table->stime = stime;
+ table->last_pid_info = NULL;
+ table->last_pid = 0;
+ table->fg_time = fg_time;
+ table->bg_time = bg_time;
+ table->fg_count = fg_count;
+ table->cpu_info =
+ g_array_new(FALSE, FALSE, sizeof(struct heart_cpu_info *));
+
+ for (i = 0; i < CPU_ARRAY_MAX; i++) {
+ struct heart_cpu_info *ci;
+
+ ret = sscanf(buf + len, "%lu %lu %d %d ", &utime, &stime, &pid, &state);
+ if (ret <= 0) {
+ _E("file read fail %s", buf + len);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ ci = malloc(sizeof(struct heart_cpu_info));
+ if (!ci) {
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ ci->utime = utime;
+ ci->stime = stime;
+ ci->pid = pid;
+ ci->state = state;
+ len += heart_cpu_read_length(buf + len, 4);
+ g_array_append_val(table->cpu_info, ci);
+ }
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ g_array_free(table->cpu_info, TRUE);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ g_hash_table_insert(hashtable, (gpointer)table->appid, (gpointer)table);
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_save_to_file(GHashTable *hashtable, char *filename)
+{
+ int i, len, ret, array_len;
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+ struct heart_cpu_table *table;
+ FILE *fp;
+ char buf[CPU_DATA_MAX] = {0, };
+
+ fp = fopen(filename, "w");
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&iter, hashtable);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ table = (struct heart_cpu_table *)value;
+ array_len = table->cpu_info->len;
+ len = snprintf(buf, CPU_DATA_MAX, "%s %s %lu %lu %lu %lu %lu %lu %d ",
+ table->appid, table->pkgid,
+ table->total_utime,
+ table->total_stime,
+ table->utime,
+ table->stime,
+ table->fg_time,
+ table->bg_time,
+ table->fg_count);
+
+ for (i = 0; i < CPU_ARRAY_MAX; i++) {
+ struct heart_cpu_info *ci;
+ if (array_len <= i) {
+ len += snprintf(buf + len, CPU_DATA_MAX - len, "0 0 0 0 ");
+ } else {
+ ci = g_array_index(table->cpu_info, struct heart_cpu_info *, i);
+ if (!ci)
+ break;
+ len += snprintf(buf + len, CPU_DATA_MAX - len, "%lu %lu %d %d ",
+ ci->utime,
+ ci->stime,
+ ci->pid,
+ ci->state);
+ }
+ }
+ len += snprintf(buf + len, CPU_DATA_MAX - len, "\n");
+ fputs(buf, fp);
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_hashtable_renew(GHashTable *hashtable, time_t now)
+{
+ int ret;
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+ struct heart_cpu_table *table;
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ g_hash_table_iter_init(&iter, hashtable);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ table = (struct heart_cpu_table *)value;
+ table->total_utime = 0;
+ table->total_stime = 0;
+ table->last_renew_time = now;
+ table->fg_count = 0;
+ table->fg_time = 0;
+ table->bg_time = 0;
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+void heart_cpu_update(struct logging_table_form *data, void *user_data)
+{
+ int ret;
+ pid_t pid;
+ int state;
+ unsigned long utime, stime;
+ unsigned long utime_diff = 0, stime_diff = 0;
+ time_t curr_time = logging_get_time(CLOCK_BOOTTIME);
+ struct heart_cpu_table *table;
+ struct heart_cpu_info *ci = NULL;
+ GHashTable *cpu_usage_list = NULL;
+
+ if (user_data)
+ cpu_usage_list = (GHashTable *)user_data;
+ else
+ cpu_usage_list = heart_cpu_app_list;
+
+ _D("%s %s %d %s", data->appid, data->pkgid, data->time, data->data);
+ if (sscanf(data->data, "%lu %lu %d %d ", &utime, &stime, &pid, &state) < 0) {
+ _E("sscanf failed");
+ return;
+ }
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+ table =
+ g_hash_table_lookup(cpu_usage_list, data->appid);
+ /* update */
+ if (table) {
+
+ if (table->last_renew_time > data->time)
+ goto unlock_exit;
+
+ ci = find_pid_info(table, pid);
+ if (table->last_pid_info && ci) {
+ utime_diff = utime - ci->utime;
+ table->utime += utime_diff;
+ table->total_utime += utime_diff;
+ stime_diff = stime - ci->stime;
+ table->stime += stime_diff;
+ table->total_stime += stime_diff;
+ ci->utime = utime;
+ ci->stime = stime;
+ if (ci->state == BACKG) {
+ table->bg_time += utime_diff + stime_diff;
+ if (state == FOREG)
+ table->fg_count++;
+ } else
+ table->fg_time += utime_diff + stime_diff;
+ ci->state = state;
+ } else {
+ table->utime += utime;
+ table->total_utime += utime;
+ table->stime += stime;
+ table->total_stime += stime;
+ if (table->last_pid_info)
+ heart_cpu_remove_last_pid_info_exited(table);
+ ci = malloc(sizeof(struct heart_cpu_info));
+ if (!ci) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+ ci->pid = pid;
+ ci->utime = utime;
+ ci->stime = stime;
+ ci->state = state;
+ if (ci->state == FOREG)
+ table->fg_count++;
+ table->last_pid_info = g_slist_prepend(table->last_pid_info, ci);
+ table->last_pid = pid;
+ }
+ } else {
+ table = malloc(sizeof(struct heart_cpu_table));
+
+ if (!table) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", data->appid) < 0) {
+ free(table);
+ _E("snprintf failed");
+ goto unlock_exit;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", data->pkgid) < 0) {
+ free(table);
+ _E("snprintf failed");
+ goto unlock_exit;
+ }
+ table->total_utime = utime;
+ table->total_stime = stime;
+ table->utime = utime;
+ table->stime = stime;
+ table->fg_count = 0;
+ table->fg_time = 0;
+ table->bg_time = 0;
+ if (state == FOREG)
+ table->fg_count = 1;
+
+ table->cpu_info =
+ g_array_new(FALSE, FALSE, sizeof(struct heart_cpu_info *));
+ if (!table->cpu_info) {
+ free(table);
+ _E("g_array_new failed");
+ goto unlock_exit;
+ }
+
+ ci = malloc(sizeof(struct heart_cpu_info));
+ if (!ci) {
+ _E("malloc failed");
+ free(table);
+ goto unlock_exit;
+ }
+ ci->pid = pid;
+ ci->utime = utime;
+ ci->stime = stime;
+ ci->state = state;
+ table->last_pid_info = NULL;
+ table->last_pid_info = g_slist_prepend(table->last_pid_info, ci);
+ table->last_pid = pid;
+
+ g_hash_table_insert(cpu_usage_list, (gpointer)table->appid, (gpointer)table);
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+
+ if (last_file_commit_time + HEART_CPU_SAVE_INTERVAL < curr_time) {
+ /* all hash table update and make new array */
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+ struct heart_cpu_table *search;
+ struct heart_cpu_info *ci;
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+
+ g_hash_table_iter_init(&iter, cpu_usage_list);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ search = (struct heart_cpu_table *)value;
+
+ ci = malloc(sizeof(struct heart_cpu_info));
+
+ if (!ci) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+ /* make new array node */
+ ci->pid = search->last_pid;
+ ci->utime = search->utime;
+ ci->stime = search->stime;
+ search->utime = 0;
+ search->stime = 0;
+ /* hashtable sliding : remove last node and make new one */
+ g_array_remove_index(search->cpu_info, CPU_ARRAY_MAX - 1);
+ g_array_prepend_val(search->cpu_info, ci);
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+ /* rewrite hashtable list file */
+ ret = heart_cpu_save_to_file(cpu_usage_list, HEART_CPU_DATA_FILE);
+ if (ret) {
+ _E("save to file failed");
+ goto unlock_exit;
+ }
+
+ last_file_commit_time = curr_time;
+ }
+
+ return;
+
+unlock_exit:
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+}
+
+struct heart_cpu_data *heart_cpu_get_data(char *appid, enum heart_data_period period)
+{
+ int index, i, ret;
+ struct heart_cpu_table *table;
+ struct heart_cpu_data *data;
+
+ if (!appid) {
+ _E("Wrong arguments!");
+ return NULL;
+ }
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return NULL;
+ }
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ return NULL;
+ }
+
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ return NULL;
+ }
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return NULL;
+ }
+ table = g_hash_table_lookup(heart_cpu_app_list, (gconstpointer)appid);
+ if (!table) {
+ goto unlock_exit;
+ }
+ data = malloc(sizeof(struct heart_cpu_data));
+ if (!data) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+ if (snprintf(data->appid, MAX_APPID_LENGTH, "%s", table->appid) < 0) {
+ _E("snprintf failed");
+ free(data);
+ goto unlock_exit;
+ }
+ if (snprintf(data->pkgid, MAX_PKGNAME_LENGTH, "%s", table->pkgid) < 0) {
+ _E("snprintf failed");
+ free(data);
+ goto unlock_exit;
+ }
+ if (period == DATA_LATEST) {
+ data->utime = table->total_utime;
+ data->stime = table->total_stime;
+ } else {
+ data->utime = table->utime;
+ data->stime = table->stime;
+ i = table->cpu_info->len;
+ if (i == 0) {
+ free(data);
+ goto unlock_exit;
+ }
+ if (i < index)
+ index = i;
+ for (i = 0; i < index; i++) {
+ struct heart_cpu_info *cpu_info;
+ cpu_info =
+ g_array_index(table->cpu_info, struct heart_cpu_info *, i);
+ if (!cpu_info)
+ break;
+ data->utime += cpu_info->utime;
+ data->stime += cpu_info->stime;
+ }
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ free(data);
+ return NULL;
+ }
+ return data;
+unlock_exit:
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return NULL;
+ }
+ return NULL;
+}
+
+static int compare_usage(const struct heart_app_usage *lau_a,
+ const struct heart_app_usage *lau_b)
+{
+ if (lau_a->point != lau_b->point)
+ return (lau_b->point - lau_a->point);
+
+ return 0;
+}
+
+/*
+ * Calculate application usage using frequency and time
+ */
+static double heart_cpu_get_point(int freq, int time)
+{
+ double weightForFrequence = 3;
+ double point = 0;
+ point = sqrt(time + (freq*weightForFrequence));
+ return point;
+}
+
+int heart_cpu_get_table(GArray *arrays, enum heart_data_period period)
+{
+ int index, i, ret;
+ gpointer value;
+ gpointer key;
+ GHashTableIter h_iter;
+ struct heart_cpu_table *table;
+ struct heart_cpu_data *cdata;
+
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&h_iter, heart_cpu_app_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+
+ table = (struct heart_cpu_table *)value;
+ cdata = malloc(sizeof(struct heart_cpu_data));
+ if (!cdata) {
+ _E("malloc failed");
+ goto unlock_out_of_memory_exit;
+ }
+ if (snprintf(cdata->appid, MAX_APPID_LENGTH, "%s", table->appid) < 0) {
+ _E("snprintf failed");
+ free(cdata);
+ goto unlock_out_of_memory_exit;
+ }
+ if (snprintf(cdata->pkgid, MAX_PKGNAME_LENGTH, "%s", table->pkgid) < 0) {
+ _E("snprintf failed");
+ free(cdata);
+ goto unlock_out_of_memory_exit;
+ }
+ if (period == DATA_LATEST) {
+ cdata->utime = table->total_utime;
+ cdata->stime = table->total_stime;
+ } else {
+ cdata->utime = table->utime;
+ cdata->stime = table->stime;
+ i = table->cpu_info->len;
+ if (i == 0) {
+ free(cdata);
+ break;
+ }
+ if (i < index)
+ index = i;
+ for (i = 0; i < index; i++) {
+ struct heart_cpu_info *cpu_info;
+ cpu_info =
+ g_array_index(table->cpu_info, struct heart_cpu_info *, i);
+ if (!cpu_info)
+ break;
+ cdata->utime += cpu_info->utime;
+ cdata->stime += cpu_info->stime;
+ }
+ }
+ g_array_append_val(arrays, cdata);
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+unlock_out_of_memory_exit:
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+}
+
+int heart_cpu_get_appusage_list(GHashTable *lists, int top)
+{
+ int index = top, i, ret;
+ gpointer value;
+ gpointer key;
+ GHashTableIter h_iter;
+ struct heart_cpu_table *table;
+ struct heart_app_usage lau;
+ GArray *app_lists = NULL;
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ app_lists = g_array_new(false, false, sizeof(struct heart_app_usage));
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&h_iter, heart_cpu_app_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+
+ table = (struct heart_cpu_table *)value;
+ if (!table->fg_count)
+ continue;
+
+ lau.appid = table->appid;
+ lau.pkgid = table->pkgid;
+ lau.fg_count = table->fg_count;
+ lau.used_time = table->fg_time;
+ lau.point = (int)heart_cpu_get_point(lau.fg_count, lau.used_time);
+ /*
+ * make all application lists with weighted point value excepting service application
+ */
+ g_array_append_val(app_lists, lau);
+ }
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ g_array_free(app_lists, true);
+ return RESOURCED_ERROR_FAIL;
+ }
+ if (app_lists->len < top) {
+ _I("too small data for making app usage lists");
+ g_array_free(app_lists, true);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ g_array_sort(app_lists, (GCompareFunc)compare_usage);
+
+ if (!top)
+ index = app_lists->len;
+
+ /*
+ * replace application usage lists with sorted usage arrays
+ */
+ g_hash_table_remove_all(lists);
+ for (i = 0; i < index; i++) {
+ struct heart_app_usage *usage = &g_array_index(app_lists, struct heart_app_usage, i);
+ _D("appid : %s, point : %d", usage->appid, usage->point);
+ g_hash_table_insert(lists, g_strndup(usage->appid, strlen(usage->appid)), GINT_TO_POINTER(1));
+ }
+ g_array_free(app_lists, true);
+ return RESOURCED_ERROR_NONE;
+}
+
+static DBusMessage *edbus_heart_get_cpu_data(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int period, index, i, ret;
+ char *appid;
+ struct heart_cpu_table *table;
+
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ time_t utime = 0, stime = 0;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+
+ dbus_message_iter_init_append(reply, &iter);
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ table = g_hash_table_lookup(heart_cpu_app_list, (gconstpointer)appid);
+ if (!table) {
+ goto unlock_exit;
+ }
+ if (period == DATA_LATEST) {
+ utime = table->total_utime;
+ stime = table->total_stime;
+ } else {
+ utime = table->utime;
+ stime = table->stime;
+ i = table->cpu_info->len;
+ if (i < index)
+ index = i;
+ for (i = 0; i < index; i++) {
+ struct heart_cpu_info *ci;
+ ci = g_array_index(table->cpu_info, struct heart_cpu_info *, i);
+ if (!ci)
+ break;
+ utime += ci->utime;
+ stime += ci->stime;
+ }
+ }
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &utime);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &stime);
+unlock_exit:
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ return reply;
+}
+
+static DBusMessage *edbus_heart_get_cpu_data_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int period, index, i, ret;
+ gpointer value;
+ gpointer key;
+ GHashTableIter h_iter;
+ struct heart_cpu_table *table;
+
+ DBusMessage *reply;
+ DBusMessageIter d_iter;
+ DBusMessageIter arr;
+ char *appid;
+ unsigned long utime, stime, ftime, total;
+ utime = stime = ftime = total = 0;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ heart_cpu_update_app_list(NULL);
+
+ logging_save_to_storage(true);
+ /* update data list from db */
+ logging_update(true);
+
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+
+ dbus_message_iter_init_append(reply, &d_iter);
+ dbus_message_iter_open_container(&d_iter, DBUS_TYPE_ARRAY, "(sii)", &arr);
+ ret = pthread_mutex_lock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ dbus_message_iter_close_container(&d_iter, &arr);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ g_hash_table_iter_init(&h_iter, heart_cpu_app_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+ DBusMessageIter sub;
+
+ table = (struct heart_cpu_table *)value;
+ if (!table)
+ break;
+ if (period == DATA_LATEST) {
+ utime = table->total_utime;
+ stime = table->total_stime;
+ } else {
+ utime = table->utime;
+ stime = table->stime;
+ i = table->cpu_info->len;
+ if (i < index)
+ index = i;
+ for (i = 0; i < index; i++) {
+ struct heart_cpu_info *ci;
+ ci = g_array_index(table->cpu_info, struct heart_cpu_info *, i);
+ if (!ci)
+ break;
+ utime += ci->utime;
+ stime += ci->stime;
+ }
+ }
+ ftime = table->fg_time;
+ total = utime + stime;
+ if (total == 0)
+ continue;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ appid = table->appid;
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &total);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &ftime);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ ret = pthread_mutex_unlock(&heart_cpu_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ dbus_message_iter_close_container(&d_iter, &arr);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ dbus_message_iter_close_container(&d_iter, &arr);
+
+ return reply;
+}
+
+static DBusMessage *edbus_heart_reset_cpu_data(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ if (!heart_cpu_app_list) {
+ _E("empty app list");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ if (!g_hash_table_size(heart_cpu_app_list)) {
+ _E("hash table is empty");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ ret = heart_cpu_hashtable_renew(heart_cpu_app_list, time(NULL));
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+
+ return reply;
+}
+
+static DBusMessage *edbus_heart_update_cpu_data(E_DBus_Object *obj, DBusMessage *msg)
+{
+
+ int ret = 0;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ heart_cpu_update_app_list(NULL);
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+static DBusMessage *edbus_heart_save_to_file(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ ret = heart_cpu_save_to_file(heart_cpu_app_list, HEART_CPU_DATA_FILE);
+ if (ret) {
+ _E("save to file failed");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ last_file_commit_time = logging_get_time(CLOCK_BOOTTIME);
+
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { "GetCpuData", "si", "ii", edbus_heart_get_cpu_data },
+ { "GetCpuDataList", "i", "a(sii)", edbus_heart_get_cpu_data_list },
+ { "ResetCpuData", NULL, "i", edbus_heart_reset_cpu_data },
+ { "UpdateCpuData", NULL, "i", edbus_heart_update_cpu_data },
+ { "SaveCpuData", NULL, "i", edbus_heart_save_to_file },
+};
+
+static int heart_cpu_reset(void *data)
+{
+ return heart_cpu_hashtable_renew(heart_cpu_app_list, time(NULL));
+}
+
+static int heart_cpu_init(void *data)
+{
+ int ret;
+
+ ret = logging_module_init(CPU_NAME, ONE_DAY, TEN_MINUTE, heart_cpu_update, TEN_MINUTE);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("logging module init failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+ if (!heart_cpu_app_list) {
+ heart_cpu_app_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ heart_free_value);
+
+ /* make hash from file */
+ ret = heart_cpu_read_from_file(heart_cpu_app_list, HEART_CPU_DATA_FILE);
+
+ if (ret == RESOURCED_ERROR_OUT_OF_MEMORY) {
+ _E("heart_cpu_init failed");
+ return ret;
+ }
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed",
+ RESOURCED_PATH_LOGGING);
+ }
+
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, heart_cpu_service_launch);
+ register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, heart_cpu_foreground_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, heart_cpu_background_state);
+ register_notifier(RESOURCED_NOTIFIER_DATA_UPDATE, heart_cpu_update_app_list);
+ register_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_cpu_reset);
+
+ last_file_commit_time = logging_get_time(CLOCK_BOOTTIME);
+
+ _D("heart cpu init finished");
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_exit(void *data)
+{
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, heart_cpu_service_launch);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, heart_cpu_foreground_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, heart_cpu_background_state);
+ unregister_notifier(RESOURCED_NOTIFIER_DATA_UPDATE, heart_cpu_update_app_list);
+ unregister_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_cpu_reset);
+
+ if (heart_cpu_app_list) {
+ heart_cpu_save_to_file(heart_cpu_app_list, HEART_CPU_DATA_FILE);
+ if (heart_cpu_app_list)
+ g_hash_table_destroy(heart_cpu_app_list);
+ }
+
+ logging_module_exit();
+
+ _D("heart cpu exit");
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_cpu_dump(FILE *fp, int mode, void *data)
+{
+ time_t starttime;
+ char timestr[80];
+ struct tm loc_tm;
+ static GHashTable *cpu_usage_list;
+ gpointer value;
+ gpointer key;
+ GHashTableIter h_iter;
+ struct heart_cpu_table *table;
+
+ starttime = time(NULL);
+
+ starttime -= mode;
+ localtime_r(&starttime, &loc_tm);
+ /* print timestamp */
+ strftime(timestr, sizeof(timestr),
+ "%Y-%m-%d %H:%M:%S%z", &loc_tm);
+
+ cpu_usage_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ free);
+
+
+ logging_read_foreach(CPU_NAME, NULL, NULL, starttime, 0,
+ heart_cpu_update, cpu_usage_list);
+
+ if (!g_hash_table_size(cpu_usage_list)) {
+ _E("hash table is empty");
+ return 0;
+ }
+
+ LOG_DUMP(fp, "[CPU USAGE LISTS] since %s\n", timestr);
+ LOG_DUMP(fp, "appid pkgid total fg_count fg_time\n");
+ g_hash_table_iter_init(&h_iter, cpu_usage_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+
+ table = (struct heart_cpu_table *)value;
+ if (!table)
+ break;
+ LOG_DUMP(fp, "%s %s %ld %d %ld\n", table->appid, table->pkgid,
+ table->total_utime + table->total_stime,
+ table->fg_count, table->fg_time);
+ }
+ fflush(fp);
+ g_hash_table_destroy(cpu_usage_list);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_cpu_ops = {
+ .name = "CPU",
+ .init = heart_cpu_init,
+ .dump = heart_cpu_dump,
+ .exit = heart_cpu_exit,
+};
+HEART_MODULE_REGISTER(&heart_cpu_ops)
--- /dev/null
+/*
+ * 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 heart-memory.c
+ *
+ * @desc heart memory module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <glib.h>
+#include <Ecore.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "notifier.h"
+#include "proc-common.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "decision-memory.h"
+#include "edbus-handler.h"
+#include "smaps.h"
+
+#include <sqlite3.h>
+#include <time.h>
+
+#define MEM_NAME "memory"
+#define MEM_DATA_MAX 1024
+#define MEM_ARRAY_MAX 24
+#define MEM_FILE_SEPERATOR '@'
+
+#define HEART_MEMORY_SAVE_INTERVAL 3600 /* 1 hour */
+#define HEART_MEMORY_FILE HEART_FILE_PATH"/.memory.dat"
+
+struct heart_memory_info {
+ unsigned int max_pss;
+ unsigned int avg_pss;
+ unsigned int max_uss;
+ unsigned int avg_uss;
+};
+
+struct heart_memory_table {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ unsigned int total_pss;
+ unsigned int max_pss;
+ unsigned int latest_pss;
+ unsigned int total_uss;
+ unsigned int max_uss;
+ unsigned int latest_uss;
+ unsigned int count;
+ int renew;
+ GArray *memory_info;
+};
+
+static GHashTable *heart_memory_app_list;
+static pthread_mutex_t heart_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
+static time_t last_file_update_time;
+static Ecore_Timer *heart_memory_update_timer = NULL;
+
+struct heart_memory_table *heart_memory_find_info(GHashTable *hashtable, char *appid)
+{
+ int ret;
+ struct heart_memory_table *table;
+
+ if (!hashtable) {
+ _E("hashtable is NULL");
+ return NULL;
+ }
+
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return NULL;
+ }
+
+ table = g_hash_table_lookup(hashtable, (gconstpointer)appid);
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return NULL;
+ }
+
+ return table;
+}
+
+static void heart_memory_free_value(gpointer value)
+{
+ struct heart_memory_table * table = (struct heart_memory_table *)value;
+
+ if (!table)
+ return;
+ free(table);
+}
+
+static int heart_memory_read_length(char *buf, int count)
+{
+ int i, find = 0;
+ int len = strlen(buf);
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] == ' ')
+ find++;
+
+ if (find == count)
+ return i+1;
+ }
+
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int heart_memory_read_from_file(GHashTable *hashtable, char *filename)
+{
+ int i, len, ret, result;
+ unsigned int total_pss, max_pss, avg_pss;
+ unsigned int total_uss, max_uss, avg_uss;
+ unsigned int count;
+ FILE *fp;
+ struct heart_memory_table *table;
+ char appid[MAX_APPID_LENGTH] = {0, };
+ char pkgid[MAX_PKGNAME_LENGTH] = {0, };
+ char buf[MEM_DATA_MAX] = {0, };
+
+ fp = fopen(filename, "r");
+
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ while (fgets(buf, MEM_DATA_MAX, fp)) {
+ table = malloc(sizeof(struct heart_memory_table));
+
+ if (!table) {
+ _E("malloc failed");
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* make return values */
+ result = sscanf(buf, "%s %s %u %u %u %u %u ", appid, pkgid,
+ &total_pss, &max_pss, &total_uss, &max_uss, &count);
+
+ if (result < 0) {
+ _E("sscanf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", appid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", pkgid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ len = heart_memory_read_length(buf, 7);
+
+ if (len < 0) {
+ _E("7 space read length failed %s", buf);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ table->total_pss = total_pss;
+ table->max_pss = max_pss;
+ table->latest_pss = 0;
+ table->total_uss = total_uss;
+ table->max_uss = max_uss;
+ table->latest_uss = 0;
+ table->count = count;
+ table->memory_info =
+ g_array_new(false, false, sizeof(struct heart_memory_info *));
+
+ for (i = 0; i < MEM_ARRAY_MAX; i++) {
+ struct heart_memory_info *mi;
+
+ result = sscanf(buf + len, "%u %u %u %u ",
+ &max_pss, &avg_pss, &max_uss, &avg_uss);
+
+ if (result <= 0) {
+ _E("file read fail %s", buf + len);
+ g_array_free(table->memory_info, true);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ mi = malloc(sizeof(struct heart_memory_info));
+
+ if (!mi) {
+ _E("malloc failed");
+ g_array_free(table->memory_info, true);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ mi->max_pss = max_pss;
+ mi->avg_pss = avg_pss;
+ mi->max_uss = max_uss;
+ mi->avg_uss = avg_uss;
+
+ len += heart_memory_read_length(buf + len, 4);
+
+ g_array_append_val(table->memory_info, mi);
+ }
+
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ g_array_free(table->memory_info, true);
+ free(table);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_insert(hashtable, (gpointer)table->appid, (gpointer)table);
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_memory_save_to_file(GHashTable *hashtable, char *filename)
+{
+ int i, len, ret, array_len;
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+ struct heart_memory_table *table;
+ FILE *fp;
+ char buf[MEM_DATA_MAX] = {0, };
+
+ fp = fopen(filename, "w");
+ if (!fp) {
+ _E("%s fopen failed %d", filename, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&iter, hashtable);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ table = (struct heart_memory_table *)value;
+
+ len = snprintf(buf, MEM_DATA_MAX, "%s %s %u %u %u %u %u ",
+ table->appid, table->pkgid,
+ table->total_pss, table->max_pss,
+ table->total_uss, table->max_uss,
+ table->count);
+
+ array_len = table->memory_info->len;
+
+ for (i = 0; i < MEM_ARRAY_MAX; i++) {
+ struct heart_memory_info *mi;
+
+ if (array_len <= i)
+ len += snprintf(buf + len, MEM_DATA_MAX - len, "0 0 0 0 ");
+ else {
+ mi = g_array_index(table->memory_info, struct heart_memory_info *, i);
+
+ len += snprintf(buf + len, MEM_DATA_MAX - len, "%u %u %u %u ",
+ mi->max_pss, mi->avg_pss,
+ mi->max_uss, mi->avg_uss);
+ }
+ }
+
+ len += snprintf(buf + len, MEM_DATA_MAX - len, "%c\n", MEM_FILE_SEPERATOR);
+
+ fputs(buf, fp);
+ }
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ fclose(fp);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+void heart_memory_fill_array(struct logging_table_form *data, void *user_data)
+{
+ int i;
+ unsigned int pss, uss;
+ struct heart_memory_data *md = NULL;
+ GArray *send = NULL;
+
+ _E("data : %s %s %d %s", data->appid, data->pkgid, (int)data->time, data->data);
+ send = (GArray *)user_data;
+
+ for (i = 0; i < send->len; i++) {
+ struct heart_memory_data *loop;
+ loop = g_array_index(send, struct heart_memory_data *, i);
+
+ if (!strcmp(loop->appid, data->appid)) {
+ md = loop;
+ break;
+ }
+ }
+
+ if (sscanf(data->data, "%u %u", &pss, &uss) < 0) {
+ _E("sscanf failed");
+ return;
+ }
+
+ if (!md) {
+ md = malloc(sizeof(struct heart_memory_data));
+
+ if (!md) {
+ _E("malloc failed");
+ return;
+ }
+
+ if (snprintf(md->appid, MAX_APPID_LENGTH, "%s", data->appid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ return;
+ }
+
+ if (snprintf(md->pkgid, MAX_PKGNAME_LENGTH, "%s", data->pkgid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ return;
+ }
+
+ md->max_pss = pss;
+ md->avg_pss = pss;
+ md->max_uss = uss;
+ md->avg_uss = uss;
+
+ g_array_append_val(send, md);
+ } else {
+ if (md->max_pss < pss)
+ md->max_pss = pss;
+
+ md->avg_pss = (md->avg_pss + pss) / 2;
+
+ if (md->max_uss < uss)
+ md->max_uss = uss;
+
+ md->avg_uss = (md->avg_uss + uss) / 2;
+ }
+}
+
+int logging_memory_get_foreach(GArray *arrays, enum heart_data_period period)
+{
+ int ret;
+ time_t curr_time = time(NULL);
+
+ switch (period) {
+ case DATA_LATEST:
+ break;
+ case DATA_3HOUR:
+ curr_time -= 10800;
+ break;
+ case DATA_6HOUR:
+ curr_time -= 21600;
+ break;
+ case DATA_12HOUR:
+ curr_time -= 43200;
+ break;
+ case DATA_1DAY:
+ curr_time -= 86400;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = logging_read_foreach(MEM_NAME, NULL, NULL, curr_time, 0, heart_memory_fill_array, arrays);
+
+ if (ret) {
+ _E("failed logging_read_foreach");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int heart_memory_get_query(GArray *arrays, enum heart_data_period period)
+{
+ int count, result;
+ time_t curr_time = time(NULL);
+ sqlite3 *heart_db;
+ sqlite3_stmt *stmt = NULL;
+ gpointer value;
+ gpointer key;
+ char *data;
+ unsigned int pss, uss;
+ GHashTableIter h_iter;
+ struct heart_memory_data *md;
+ struct heart_memory_table *table;
+ char buf[MEM_DATA_MAX] = {0, };
+
+ switch (period) {
+ case DATA_LATEST:
+ break;
+ case DATA_3HOUR:
+ curr_time -= 10800;
+ break;
+ case DATA_6HOUR:
+ curr_time -= 21600;
+ break;
+ case DATA_12HOUR:
+ curr_time -= 43200;
+ break;
+ case DATA_1DAY:
+ curr_time -= 86400;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ result = sqlite3_open(LOGGING_DB_FILE_NAME, &heart_db);
+ if (result != SQLITE_OK) {
+ _E("Can't open database %s: %s\n", MEM_NAME,
+ sqlite3_errmsg(heart_db));
+ sqlite3_close(heart_db);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&h_iter, heart_memory_app_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+ table = (struct heart_memory_table *)value;
+
+ md = malloc(sizeof(struct heart_memory_data));
+
+ if (!md) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(md->appid, MAX_APPID_LENGTH, "%s", table->appid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(md->pkgid, MAX_PKGNAME_LENGTH, "%s", table->pkgid) < 0) {
+ _E("asprintf failed");
+ free(md);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ count = 0;
+ md->max_pss = 0;
+ md->avg_pss = 0;
+ md->max_uss = 0;
+ md->avg_uss = 0;
+
+ snprintf(buf, MEM_DATA_MAX, "select * from memory where appid = \'%s\' AND time > %d",
+ table->appid, (int)curr_time);
+
+ /* search from db */
+ if (sqlite3_prepare_v2(heart_db, buf, -1, &stmt, NULL) != SQLITE_OK) {
+ _E("select failed");
+ free(md);
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ do {
+ result = sqlite3_step(stmt);
+ switch (result) {
+ case SQLITE_ROW:
+ data = (char *)sqlite3_column_text(stmt, 3);
+
+ if (sscanf(data, "%u %u", &pss, &uss) < 0) {
+ _E("sscanf failed");
+ free(md);
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (md->max_pss < pss)
+ md->max_pss = pss;
+
+ if (md->max_uss < uss)
+ md->max_uss = uss;
+
+ md->avg_pss += pss;
+ md->avg_uss += uss;
+ count++;
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ _E("select %s table failed %s",
+ MEM_NAME, sqlite3_errmsg(heart_db));
+ /* FALLTHROUGH */
+ default:
+ free(md);
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ } while (result == SQLITE_ROW);
+ if (count) {
+ md->avg_pss /= count;
+ md->avg_uss /= count;
+ }
+
+ g_array_append_val(arrays, md);
+ }
+
+ sqlite3_finalize(stmt);
+
+ sqlite3_close(heart_db);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+struct heart_memory_data *heart_memory_get_data(char *appid, enum heart_data_period period)
+{
+ int index, i, ret, count, time;
+ unsigned int max_pss = 0, avg_pss = 0, total_pss = 0;
+ unsigned int max_uss = 0, avg_uss = 0, total_uss = 0;
+ struct heart_memory_table *table;
+ struct heart_memory_data *md;
+
+ if (!appid) {
+ _E("Wrong message arguments!");
+ return NULL;
+ }
+
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return NULL;
+ }
+
+ if (!heart_memory_app_list) {
+ _E("hashtable heart_memory_app_list is NULL");
+ return NULL;
+ }
+
+ /* read from hash and make reply hash */
+ /* loop in file read and make reply */
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return NULL;
+ }
+
+ table = g_hash_table_lookup(heart_memory_app_list, (gconstpointer)appid);
+ if (!table) {
+ goto unlock_exit;
+ }
+ md = malloc(sizeof(struct heart_memory_data));
+ if (!md) {
+ _E("malloc failed");
+ goto unlock_exit;
+ }
+
+ if (snprintf(md->appid, MAX_APPID_LENGTH, "%s", table->appid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ goto unlock_exit;
+ }
+
+ if (snprintf(md->pkgid, MAX_PKGNAME_LENGTH, "%s", table->pkgid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ goto unlock_exit;
+ }
+
+ count = table->count;
+ total_pss = table->total_pss;
+ max_pss = table->max_pss;
+ avg_pss = total_pss / count;
+ total_uss = table->total_uss;
+ max_uss = table->max_uss;
+ avg_uss = total_uss / count;
+ time = 1;
+
+ if (period != DATA_LATEST) {
+ i = table->memory_info->len;
+
+ if (i < index)
+ index = i;
+
+ for (i = 0; i < index; i++) {
+ struct heart_memory_info *mi;
+
+ mi = g_array_index(table->memory_info, struct heart_memory_info *, i);
+
+ if (mi->max_pss || mi->avg_pss ||
+ mi->max_uss || mi->avg_uss)
+ time++;
+
+ if (max_pss < mi->max_pss)
+ max_pss = mi->max_pss;
+
+ avg_pss += mi->avg_pss;
+
+ if (max_uss < mi->max_uss)
+ max_uss = mi->max_uss;
+
+ avg_uss += mi->avg_uss;
+ }
+
+ if (time) {
+ avg_pss /= time;
+ avg_uss /= time;
+ }
+ }
+
+ md->max_pss = max_pss;
+ md->avg_pss = avg_pss;
+ md->max_uss = max_uss;
+ md->avg_uss = avg_uss;
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ free(md);
+ return NULL;
+ }
+ return md;
+
+unlock_exit:
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return NULL;
+ }
+ return NULL;
+}
+
+int heart_memory_get_latest_data(char *appid, unsigned int *pss, unsigned int *uss)
+{
+ int ret;
+ char *data;
+ struct heart_memory_table *table;
+
+ if (!appid) {
+ _E("Wrong message arguments!");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!heart_memory_app_list) {
+ _E("hashtable heart_memory_app_list is NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* read from hash and make reply hash */
+ /* loop in file read and make reply */
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ table = g_hash_table_lookup(heart_memory_app_list, (gconstpointer)appid);
+ if (!table) {
+ _E("NOT found in table %s", appid);
+
+ ret = logging_get_latest_in_cache(MEM_NAME, appid, &data);
+
+ if (ret) {
+ _E("logging_get_latest_in_cache failed");
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (sscanf(data, "%u %u", pss, uss) < 0) {
+ _E("sscanf failed");
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (table->latest_pss || table->latest_uss) {
+ *pss = table->latest_pss;
+ *uss = table->latest_uss;
+ } else {
+ ret = logging_get_latest_in_cache(MEM_NAME, appid, &data);
+
+ if (ret) {
+ _E("logging_get_latest_in_cache failed");
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (sscanf(data, "%u %u", pss, uss) < 0) {
+ _E("sscanf failed");
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int heart_memory_get_table(GArray *arrays, enum heart_data_period period)
+{
+ int index, i, ret, count, time;
+ gpointer value;
+ gpointer key;
+ unsigned int max_pss = 0, avg_pss = 0, total_pss = 0;
+ unsigned int max_uss = 0, avg_uss = 0, total_uss = 0;
+ GHashTableIter h_iter;
+ struct heart_memory_table *table;
+ struct heart_memory_data *md;
+
+ switch (period) {
+ case DATA_LATEST:
+ index = 0;
+ break;
+ case DATA_3HOUR:
+ index = 3;
+ break;
+ case DATA_6HOUR:
+ index = 6;
+ break;
+ case DATA_12HOUR:
+ index = 12;
+ break;
+ case DATA_1DAY:
+ index = 24;
+ break;
+ default:
+ _E("Wrong message arguments! %d", period);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* read from hash and make reply hash */
+ /* loop in file read and make reply */
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_hash_table_iter_init(&h_iter, heart_memory_app_list);
+
+ while (g_hash_table_iter_next(&h_iter, &key, &value)) {
+ table = (struct heart_memory_table *)value;
+
+ md = malloc(sizeof(struct heart_memory_data));
+ if (!md) {
+ _E("malloc failed");
+ goto unlock_out_of_memory_exit;
+ }
+
+ if (snprintf(md->appid, MAX_APPID_LENGTH, "%s", table->appid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ goto unlock_out_of_memory_exit;
+ }
+
+ if (snprintf(md->pkgid, MAX_PKGNAME_LENGTH, "%s", table->pkgid) < 0) {
+ _E("snprintf failed");
+ free(md);
+ goto unlock_out_of_memory_exit;
+ }
+
+ count = table->count;
+ total_pss = table->total_pss;
+ max_pss = table->max_pss;
+ avg_pss = total_pss / count;
+ total_uss = table->total_uss;
+ max_uss = table->max_uss;
+ avg_uss = total_uss / count;
+ time = 1;
+
+ if (period != DATA_LATEST) {
+ i = table->memory_info->len;
+
+ if (i < index)
+ index = i;
+
+ for (i = 0; i < index; i++) {
+ struct heart_memory_info *mi;
+
+ mi = g_array_index(table->memory_info, struct heart_memory_info *, i);
+
+ if (mi->max_pss || mi->avg_pss ||
+ mi->max_uss || mi->avg_uss)
+ time++;
+
+ if (max_pss < mi->max_pss)
+ max_pss = mi->max_pss;
+
+ avg_pss += mi->avg_pss;
+
+ if (max_uss < mi->max_uss)
+ max_uss = mi->max_uss;
+
+ avg_uss += mi->avg_uss;
+ }
+
+ if (time) {
+ avg_pss /= time;
+ avg_uss /= time;
+ }
+ }
+
+ md->max_pss = max_pss;
+ md->avg_pss = avg_pss;
+ md->max_uss = max_uss;
+ md->avg_uss = avg_uss;
+
+ g_array_append_val(arrays, md);
+ }
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+
+unlock_out_of_memory_exit:
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+}
+
+void heart_memory_update(struct logging_table_form *data, void *user_data)
+{
+ int ret;
+ unsigned int pss, uss;
+ time_t curr_time = logging_get_time(CLOCK_BOOTTIME);
+ struct heart_memory_table *find;
+ struct heart_memory_table *table;
+
+ if (sscanf(data->data, "%u %u", &pss, &uss) < 0) {
+ _E("sscanf failed");
+ return;
+ }
+
+ find = heart_memory_find_info(heart_memory_app_list, data->appid);
+
+ /* get last node and update it & re-insert to hash table */
+ /* update */
+ if (find) {
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+
+ if (find->renew) {
+ find->count = 0;
+ find->max_pss = 0;
+ find->total_pss = 0;
+ find->max_uss = 0;
+ find->total_uss = 0;
+ find->renew = 0;
+ }
+
+ find->total_pss += pss;
+
+ if (find->max_pss < pss)
+ find->max_pss = pss;
+
+ find->latest_pss = pss;
+
+ find->total_uss += uss;
+
+ if (find->max_uss < uss)
+ find->max_uss = uss;
+
+ find->latest_uss = uss;
+
+ find->count++;
+
+ table = find;
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+ } else {
+ table = malloc(sizeof(struct heart_memory_table));
+
+ if (!table) {
+ _E("malloc failed");
+ return;
+ }
+
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", data->appid) < 0) {
+ free(table);
+ _E("snprintf failed");
+ return;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", data->pkgid) < 0) {
+ free(table);
+ _E("snprintf failed");
+ return;
+ }
+
+ table->memory_info =
+ g_array_new(false, false, sizeof(struct heart_memory_info *));
+
+ table->total_pss = pss;
+
+ table->max_pss = pss;
+
+ table->latest_pss = pss;
+
+ table->total_uss = uss;
+
+ table->max_uss = uss;
+
+ table->latest_uss = uss;
+
+ table->count = 1;
+
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ free(table);
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+
+ g_hash_table_insert(heart_memory_app_list, (gpointer)table->appid, (gpointer)table);
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+ }
+
+ if (last_file_update_time + HEART_MEMORY_SAVE_INTERVAL < curr_time) {
+ /* all hash table update and make new array */
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+ struct heart_memory_table *search;
+ struct heart_memory_info *new_memory_info;
+
+ ret = pthread_mutex_lock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+
+ g_hash_table_iter_init(&iter, heart_memory_app_list);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ search = (struct heart_memory_table *)value;
+
+ new_memory_info = malloc(sizeof(struct heart_memory_info));
+
+ if (!new_memory_info) {
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_lock() failed, %d", ret);
+ return;
+ }
+ _E("malloc failed");
+ return;
+ }
+
+ /* make new array node */
+ new_memory_info->max_pss = search->max_pss;
+
+ if (search->count)
+ new_memory_info->avg_pss = search->total_pss / search->count;
+ else
+ new_memory_info->avg_pss = search->total_pss;
+
+ new_memory_info->max_uss = search->max_uss;
+
+ if (search->count)
+ new_memory_info->avg_uss = search->total_uss / search->count;
+ else
+ new_memory_info->avg_uss = search->total_uss;
+
+ search->renew = 1;
+
+ /* hashtable sliding : remove last node and make new one */
+ g_array_remove_index(search->memory_info, MEM_ARRAY_MAX-1);
+ g_array_prepend_val(search->memory_info, new_memory_info);
+ }
+
+ ret = pthread_mutex_unlock(&heart_memory_mutex);
+ if (ret) {
+ _E("pthread_mutex_unlock() failed, %d", ret);
+ return;
+ }
+ /* rewrite hashtable list file */
+ ret = heart_memory_save_to_file(heart_memory_app_list, HEART_MEMORY_FILE);
+ if (ret) {
+ _E("save to file failed");
+ return;
+ }
+
+ last_file_update_time = curr_time;
+ }
+}
+
+static int heart_memory_write(char *appid, char *pkgid, struct proc_status *p_data)
+{
+ _cleanup_smaps_free_ struct smaps *maps = NULL;
+ _cleanup_free_ char *info = NULL;
+ unsigned int pss = 0, uss = 0;
+ int ret;
+
+ /* For write to data crud during period */
+ /* write memory usage in proc_list */
+
+ ret = smaps_get(p_data->pid, &maps,
+ (SMAPS_MASK_PSS |
+ SMAPS_MASK_PRIVATE_CLEAN |
+ SMAPS_MASK_PRIVATE_DIRTY |
+ SMAPS_MASK_SWAP));
+ if (ret < 0) {
+ _E("Failed to get PID(%d) smaps: %s",
+ p_data->pid, strerror(-ret));
+ return ret;
+ }
+
+ pss = maps->sum[SMAPS_ID_PSS];
+ uss = maps->sum[SMAPS_ID_PRIVATE_CLEAN]
+ + maps->sum[SMAPS_ID_PRIVATE_CLEAN]
+ + maps->sum[SMAPS_ID_SWAP];
+
+ ret = asprintf(&info, "%u %u", pss, uss);
+ if (ret < 0) {
+ _E("Failed to allocate memory");
+ return -ENOMEM;
+ }
+
+ ret = logging_write(MEM_NAME, appid, pkgid, time(NULL), info);
+
+ if (ret)
+ _E("logging_write failed %d", ret);
+
+ return ret;
+}
+
+static int heart_memory_state_cb(void *data)
+{
+ int ret;
+ char *appid, *pkgid;
+ struct proc_status *ps = (struct proc_status *)data;
+
+ ret = proc_get_id_info(ps, &appid, &pkgid);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to proc_get_id_info");
+ return ret;
+ }
+
+ heart_memory_write(appid, pkgid, ps);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static Eina_Bool heart_memory_notify(void *data)
+{
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_START, NULL);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static DBusMessage *edbus_get_memory_latest(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ char *appid;
+ unsigned int pss = 0, uss = 0;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ ret = heart_memory_get_latest_data(appid, &pss, &uss);
+
+ if (ret) {
+ _E("heart_memory_get_latest_data failed %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &pss);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &uss);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_memory_data(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret, period;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ char *appid;
+ struct heart_memory_data *md;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ md = heart_memory_get_data(appid, period);
+
+ if (!md) {
+ _E("heart_memory_get_data failed %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &md->max_pss);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &md->avg_pss);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &md->max_uss);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &md->avg_uss);
+
+ free(md);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_memory_data_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int i, ret, period;
+ char *appid, *pkgid;
+ GArray *temp_array;
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ temp_array = g_array_new(false, false, sizeof(struct heart_memory_data *));
+
+ ret = heart_memory_get_table(temp_array, period);
+
+ if (ret) {
+ _E("heart_memory_get_table failed %d", ret);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssuuuu)", &arr);
+
+ for (i = 0; i < temp_array->len; i++) {
+ DBusMessageIter sub;
+ struct heart_memory_data *md;
+
+ md = g_array_index(temp_array, struct heart_memory_data *, i);
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ appid = md->appid;
+ pkgid = md->pkgid;
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &pkgid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_uss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_uss);
+
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr);
+
+ g_array_free(temp_array, true);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_memorydb(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int i, ret, period;
+ char *appid, *pkgid;
+ GArray *temp_array;
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ temp_array = g_array_new(false, false, sizeof(struct heart_memory_data *));
+ _E("start get read query!!! %d", time(NULL));
+ ret = heart_memory_get_query(temp_array, period);
+ _E("end get read query!!! %d", time(NULL));
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssuuuu)", &arr);
+
+ for (i = 0; i < temp_array->len; i++) {
+ DBusMessageIter sub;
+ struct heart_memory_data *md;
+
+ md = g_array_index(temp_array, struct heart_memory_data *, i);
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ appid = md->appid;
+ pkgid = md->pkgid;
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &pkgid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_uss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_uss);
+
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr);
+
+ g_array_free(temp_array, true);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_memoryforeach(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int i, ret, period;
+ char *appid, *pkgid;
+ GArray *temp_array;
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &period, DBUS_TYPE_INVALID);
+
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ /* read from query */
+ temp_array = g_array_new(false, false, sizeof(struct heart_memory_data *));
+ _E("start get read foreach!!! %d", time(NULL));
+ ret = logging_memory_get_foreach(temp_array, period);
+ _E("end get read foreach!!! %d", time(NULL));
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssuuuu)", &arr);
+
+ for (i = 0; i < temp_array->len; i++) {
+ DBusMessageIter sub;
+ struct heart_memory_data *md;
+
+ md = g_array_index(temp_array, struct heart_memory_data *, i);
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ appid = md->appid;
+ pkgid = md->pkgid;
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &pkgid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_pss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->max_uss);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &md->avg_uss);
+
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ dbus_message_iter_close_container(&iter, &arr);
+
+ g_array_free(temp_array, true);
+
+ return reply;
+}
+
+static DBusMessage *edbus_memory_save_to_file(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+
+ ret = heart_memory_save_to_file(heart_memory_app_list, HEART_MEMORY_FILE);
+
+ if (ret) {
+ _E("save to file failed");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { "GetMemoryLatest", "s", "uu", edbus_get_memory_latest },
+ { "GetMemoryData", "si", "uuuu", edbus_get_memory_data },
+ { "GetMemoryDataList", "i", "a(ssuuuu)", edbus_get_memory_data_list },
+ { "GetMemoryDB", "i", "a(ssuuuu)", edbus_get_memorydb },
+ { "GetMemoryforeach", "i", "a(ssuuuu)", edbus_get_memoryforeach },
+ { "SaveMemoryData", NULL, "i", edbus_memory_save_to_file },
+ /* Add methods here */
+};
+
+static int heart_memory_init(void *data)
+{
+ int ret;
+
+ ret = logging_module_init(MEM_NAME, ONE_DAY, TEN_MINUTE, heart_memory_update, TEN_MINUTE);
+
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("logging module init failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!heart_memory_app_list) {
+ heart_memory_app_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ heart_memory_free_value);
+
+ /* make hash from file */
+ ret = heart_memory_read_from_file(heart_memory_app_list, HEART_MEMORY_FILE);
+
+ if (ret == RESOURCED_ERROR_OUT_OF_MEMORY) {
+ _E("heart_memory_init failed");
+ return ret;
+ }
+ }
+
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, heart_memory_state_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, heart_memory_state_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, heart_memory_state_cb);
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_LOGGING);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ last_file_update_time = logging_get_time(CLOCK_BOOTTIME);
+
+ if (heart_memory_update_timer == NULL) {
+ _D("logging memory update timer start");
+ heart_memory_update_timer = ecore_timer_add(TEN_MINUTE, heart_memory_notify, (void *)NULL);
+ }
+
+ decision_memory_init(data);
+ _D("heart memory init finished");
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_memory_exit(void *data)
+{
+ int ret;
+
+ /* update timer delete */
+ ecore_timer_del(heart_memory_update_timer);
+ heart_memory_update_timer = NULL;
+
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, heart_memory_state_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, heart_memory_state_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, heart_memory_state_cb);
+
+ if (heart_memory_app_list) {
+ ret = heart_memory_save_to_file(heart_memory_app_list, HEART_MEMORY_FILE);
+
+ if (ret)
+ _E("save file failed %d", ret);
+
+ if (heart_memory_app_list) {
+ gpointer value;
+ gpointer key;
+ GHashTableIter iter;
+
+ g_hash_table_iter_init(&iter, heart_memory_app_list);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct heart_memory_table *table = (struct heart_memory_table *)value;
+
+ if (table->memory_info)
+ g_array_free(table->memory_info, true);
+ }
+
+ g_hash_table_destroy(heart_memory_app_list);
+ }
+ }
+
+ logging_module_exit();
+
+ decision_memory_exit(data);
+ _D("heart memory exit");
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_memory_ops = {
+ .name = "MEMORY",
+ .init = heart_memory_init,
+ .exit = heart_memory_exit,
+};
+HEART_MODULE_REGISTER(&heart_memory_ops)
--- /dev/null
+/*
+ * 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 heart-storage.c
+ *
+ * @desc heart storage module
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <Ecore.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "notifier.h"
+#include "heart.h"
+#include "logging.h"
+#include "heart-common.h"
+#include "edbus-handler.h"
+
+#include <sqlite3.h>
+
+#define HEART_STORAGE_DB "/opt/usr/dbspace/.resourced-logging-storage.db"
+#define STORAGE_NAME "storage"
+
+static bool heart_storage_initailized = false;
+static pthread_mutex_t heart_storage_verifying_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t heart_storage_verifying_thread = 0;
+static GQueue *queue = NULL;
+
+static DBusMessage *edbus_insert_log(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ char *pkgid = NULL;
+ char *data = NULL;
+ DBusMessage *reply;
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pkgid, DBUS_TYPE_STRING, &data, DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ _SD("Insert record (%s, %s)", pkgid, data);
+ ret = logging_write(STORAGE_NAME, pkgid, pkgid, time(NULL), data);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Write request failed");
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+}
+
+void heart_storage_delete_cb(struct logging_table_form *table, void *user_data)
+{
+ int ret;
+
+ if (!table) {
+ _E("the table is empty!");
+ return;
+ }
+
+ _SD("Delete callback for '%s'", table->data);
+ if (access(table->data, F_OK) == 0)
+ return;
+
+ ret = logging_delete(STORAGE_NAME, table->data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _SE("Delete request failed: %s", table->data);
+}
+
+void *heart_storage_verifying_thread_main(void *arg)
+{
+ int ret;
+ char *pkgid;
+
+ _D("Verifying thread is created!");
+ do {
+ ret = pthread_mutex_lock(&heart_storage_verifying_mutex);
+ if (ret) {
+ _E("logging storage verifying thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ pkgid = g_queue_pop_head(queue);
+ if (!pkgid)
+ break;
+
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+
+ _SD("Verify '%s'", pkgid);
+ ret = logging_read_foreach(STORAGE_NAME, NULL, pkgid, 0, 0, heart_storage_delete_cb, NULL);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Failed to read logs! : %d", ret);
+ free(pkgid);
+ } while (1);
+
+ heart_storage_verifying_thread = 0;
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+ pthread_exit((void *)0);
+}
+
+void heart_storage_verifying_thread_create(const char *data)
+{
+ char *pkgid = strndup(data, strlen(data)+1);
+ if (!pkgid) {
+ _E("Failed to allocate memory");
+ return;
+ }
+
+ int ret = pthread_mutex_lock(&heart_storage_verifying_mutex);
+ if (ret) {
+ _E("logging storage verifying thread::pthread_mutex_lock() failed, %d", ret);
+ free(pkgid);
+ return;
+ }
+
+ /* Add pkgid to queue */
+ g_queue_push_tail(queue, pkgid);
+
+ if (heart_storage_verifying_thread == 0) {
+ pthread_attr_t attr;
+ ret = pthread_attr_init(&attr);
+ if (ret < 0) {
+ _E("Failed to initialize pthread attributes, %d", ret);
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+ return;
+ }
+
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret < 0) {
+ _E("Failed to set detached state, %d", ret);
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+ return;
+ }
+
+ ret = pthread_create(&heart_storage_verifying_thread, &attr, heart_storage_verifying_thread_main, NULL);
+ if (ret < 0) {
+ _E("pthread creation for heart_storage_verifying_thread_main failed, %d", ret);
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+ return;
+ }
+ }
+
+ pthread_mutex_unlock(&heart_storage_verifying_mutex);
+}
+
+static DBusMessage *edbus_verify_log(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ char *pkgid = NULL;
+ DBusMessage *reply = NULL;
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pkgid, DBUS_TYPE_INVALID);
+ reply = dbus_message_new_method_return(msg);
+ if (!ret || !pkgid) {
+ _E("Wrong message arguments!");
+ return reply;
+ }
+ /* flush module cache */
+ logging_save_to_storage(true);
+
+ heart_storage_verifying_thread_create(pkgid);
+ return reply;
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { "Insert", "ss", NULL, edbus_insert_log },
+ { "Verify", "s", NULL, edbus_verify_log }
+};
+
+static bool is_storage_logging(void)
+{
+ static const struct module_ops *block;
+
+ if (!block) {
+ block = find_module("block");
+ if (block)
+ heart_storage_initailized = true;
+ }
+
+ return heart_storage_initailized;
+}
+
+static int heart_storage_write(void *data)
+{
+ int ret;
+ struct logging_data *ld = (struct logging_data *)data;
+
+ ret = logging_write(STORAGE_NAME, ld->appid, ld->pkgid, time(NULL), ld->data);
+ return ret;
+}
+
+static int heart_storage_init(void *data)
+{
+ int ret;
+
+ if (!is_storage_logging())
+ return RESOURCED_ERROR_UNINITIALIZED;
+
+ ret = pthread_mutex_init(&heart_storage_verifying_mutex, NULL);
+ if (ret < 0) {
+ _E("mutex_init failed %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ queue = g_queue_new();
+ if (!queue) {
+ _E("queue init failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ g_queue_init(queue);
+
+ ret = logging_module_init_with_db_path(STORAGE_NAME, FOUR_MONTH, FIVE_MINUTE, NULL, 0, HEART_STORAGE_DB);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("logging module init failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods, ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_LOGGING);
+ }
+
+ register_notifier(RESOURCED_NOTIFIER_LOGGING_WRITE, heart_storage_write);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int heart_storage_exit(void *data)
+{
+ if (!heart_storage_initailized)
+ return RESOURCED_ERROR_NONE;
+
+ _D("heart_storage exit");
+ unregister_notifier(RESOURCED_NOTIFIER_LOGGING_WRITE, heart_storage_write);
+ logging_module_exit();
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct heart_module_ops heart_storage_ops = {
+ .name = "STORAGE",
+ .init = heart_storage_init,
+ .exit = heart_storage_exit,
+};
+HEART_MODULE_REGISTER(&heart_storage_ops)
--- /dev/null
+/*
+ * resourced
+ *
+ * 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.
+ * 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 heart.c
+ *
+ * @desc start heart for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "notifier.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "heart.h"
+#include "logging.h"
+#include "resourced.h"
+#include "config-parser.h"
+#include "edbus-handler.h"
+
+static GSList *heart_module; /* module list */
+
+void heart_module_add(const struct heart_module_ops *ops)
+{
+ heart_module = g_slist_append(heart_module, (gpointer)ops);
+}
+
+void heart_module_remove(const struct heart_module_ops *ops)
+{
+ heart_module = g_slist_remove(heart_module, (gpointer)ops);
+}
+
+static const struct heart_module_ops *heart_module_find(const char *name)
+{
+ GSList *iter;
+ struct heart_module_ops *module;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ if (!strcmp(module->name, name))
+ return module;
+ }
+ return NULL;
+}
+
+static void heart_module_init(void *data)
+{
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Initialize [%s] module\n", module->name);
+ if (module->init)
+ ret = module->init(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to initialize [%s] module\n", module->name);
+ }
+}
+
+static void heart_module_exit(void *data)
+{
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Deinitialize [%s] module\n", module->name);
+ if (module->exit)
+ ret = module->exit(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to deinitialize [%s] module\n", module->name);
+ }
+}
+
+static int heart_load_config(struct parse_result *result, void *user_data)
+{
+ const struct heart_module_ops *ops;
+ int *count = (int *)user_data;
+
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, HEART_CONF_SECTION))
+ return RESOURCED_ERROR_FAIL;
+
+ ops = heart_module_find(result->name);
+ if (!ops)
+ return RESOURCED_ERROR_FAIL;
+
+ if (!strcmp(result->value, "ON"))
+ *count = *count + 1;
+ else
+ heart_module_remove(ops);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static DBusMessage *edbus_update_data_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID);
+ reply = dbus_message_new_method_return(msg);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return reply;
+ }
+
+ resourced_notify(RESOURCED_NOTIFIER_DATA_UPDATE, NULL);
+
+ logging_save_to_storage(true);
+ /* update data list from db */
+ logging_update(true);
+
+ return reply;
+}
+
+static DBusMessage *edbus_flush_cache(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessage *reply = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID);
+ reply = dbus_message_new_method_return(msg);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return reply;
+ }
+ /* flush module cache */
+ logging_save_to_storage(true);
+
+ return reply;
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { "UpdateDataList", NULL, NULL, edbus_update_data_list },
+ { "Flush", NULL, NULL, edbus_flush_cache }
+};
+
+static int resourced_heart_init(void *data)
+{
+ int ret, module_num = 0;
+
+ config_parse(HEART_CONF_FILE_PATH, heart_load_config, &module_num);
+
+ if (!module_num) {
+ _E("all heart modules have been disabled");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods, ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_LOGGING);
+ }
+
+ heart_module_init(data);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_heart_dump(FILE *fp, int mode, void *data)
+{
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ logging_save_to_storage(true);
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Dump [%s] module\n", module->name);
+ if (module->dump)
+ ret = module->dump(fp, mode, data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to dump [%s] module\n", module->name);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_heart_exit(void *data)
+{
+ heart_module_exit(data);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops heart_modules_ops = {
+ .priority = MODULE_PRIORITY_HIGH,
+ .name = "HEART",
+ .init = resourced_heart_init,
+ .dump = resourced_heart_dump,
+ .exit = resourced_heart_exit,
+};
+
+MODULE_REGISTER(&heart_modules_ops)
--- /dev/null
+[HEART]
+CPU=ON
+MEMORY=OFF
+BATTERY=ON
+
+[BATTERY_POWER_MODE]
+POWER_NORMAL_MODE=676
+POWER_SAVING_MODE=750
+ULTRA_SAVING_MODE=1947
--- /dev/null
+/*
+ * 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 decision-memory.h
+ * @desc define structures and functions for decision memory.
+ **/
+
+#ifndef __DECISION_MEMORY_H__
+#define __DECISION_MEMORY_H__
+
+struct regression_info {
+ unsigned int sum_x;
+ unsigned int sum_y;
+ unsigned int sum_xs;
+ unsigned int sum_xy;
+ unsigned int sample_count;
+ unsigned int hit;
+ float coeff_a;
+ float coeff_b;
+};
+
+struct decision_memory_info {
+ unsigned int pred_uss;
+ int warning_leak;
+
+ struct regression_info *ri;
+};
+
+int decision_memory_init(void *data);
+int decision_memory_exit(void *data);
+#endif /*__DECISION_MEMORY_H__*/
--- /dev/null
+/*
+ * 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 decision.h
+ * @desc define structures and functions for decision.
+ **/
+
+#ifndef __DECISION_H__
+#define __DECISION_H__
+
+#define DECISION_BUF_MAX 1024
+#define DECISION_MAX_ARGS 5
+
+#include "appinfo-list.h"
+
+enum {
+ DECISION_MEMORY,
+ DECISION_MAX,
+};
+
+struct decision_table {
+ struct resourced_appinfo *ai;
+ void *infos[DECISION_MAX];
+ unsigned updated;
+ unsigned offset;
+};
+
+struct decision_item {
+ int type;
+ struct resourced_appinfo *ai;
+ unsigned args[DECISION_MAX_ARGS];
+};
+
+typedef void *(*info_create_fn)(void);
+typedef void (*info_free_fn)(void *);
+typedef void (*info_update_fn)(struct decision_item *, void *);
+typedef void (*info_write_fn)(void *, char *buf, int len);
+
+struct decision_module {
+ int type;
+ info_create_fn create;
+ info_free_fn free;
+ info_update_fn update;
+ info_write_fn write;
+};
+
+int decision_module_register(const struct decision_module *dm);
+int decision_module_unregister(const struct decision_module *dm);
+int decision_update_start(void);
+int decision_queue_item_insert(struct decision_item *di);
+struct decision_item *decision_item_new(int type, const char *appid, const char *pkgname);
+#endif /*__DECISION_H__*/
--- /dev/null
+/*
+ * 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 heart.h
+ * @desc define structures and functions for logging.
+ **/
+
+#ifndef __HEART_H__
+#define __HEART_H__
+
+#include <stdio.h>
+#include <time.h>
+
+#define HEART_CONF_FILE_PATH "/etc/resourced/heart.conf"
+#define HEART_FILE_PATH "/opt/usr/data/heart"
+#define HEART_CONF_SECTION "HEART"
+
+struct heart_module_ops {
+ char *name;
+ int (*init) (void *data);
+ int (*dump) (FILE *fp, int mode, void *data);
+ int (*exit) (void *data);
+};
+
+#define HEART_MODULE_REGISTER(module) \
+static void __attribute__ ((constructor)) module_init(void) \
+{ \
+ heart_module_add(module); \
+} \
+static void __attribute__ ((destructor)) module_exit(void) \
+{ \
+ heart_module_remove(module); \
+}
+
+void heart_module_add(const struct heart_module_ops *mod);
+void heart_module_remove(const struct heart_module_ops *mod);
+
+#endif /*__HEART_H__*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 logging.h
+ * @desc define structures and functions for logging.
+ **/
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+
+#include <stdio.h>
+#include <time.h>
+#include "const.h"
+
+#ifndef CLOCK_BOOTTIME
+#define CLOCK_BOOTTIME 7
+#endif
+
+#define STRINGFY(x) #x
+#define KEY_TO_STRING(x) STRINGFY(x)
+
+#define LOGGING_DB_FILE_NAME "/opt/usr/dbspace/.resourced-logging.db"
+#define LOGGING_LEVEL_DB_FILE_NAME "/opt/usr/dbspace/.resourced-logging-leveldb"
+#define HOUR_TO_SEC(x) (x*3600)
+#define DAY_TO_SEC(x) (x*HOUR_TO_SEC(24))
+#define MONTH_TO_SEC(x) (x*DAY_TO_SEC(30))
+
+enum logging_interval {
+ ONE_MINUTE = 60,
+ FIVE_MINUTE = 300,
+ TEN_MINUTE = 600,
+ HALF_HOUR = 1800
+};
+
+enum logging_period {
+ ONE_HOUR,
+ THREE_HOUR,
+ SIX_HOUR,
+ TWELVE_HOUR,
+ ONE_DAY,
+ ONE_WEEK,
+ ONE_MONTH,
+ FOUR_MONTH
+};
+
+enum logging_operation {
+ INSERT = 0,
+ DELETE
+};
+
+struct logging_table_form {
+ char appid[MAX_APPID_LENGTH];
+ char pkgid[MAX_PKGNAME_LENGTH];
+ time_t time;
+ char *data;
+ int operation;
+};
+
+struct logging_object {
+ int ref;
+};
+
+struct logging_data {
+ char *appid;
+ char *pkgid;
+ char *data;
+};
+
+typedef void(*logging_info_cb) (struct logging_table_form *data, void *user_data);
+typedef void(*logging_listener_cb) (char *data);
+
+int logging_init(void *data);
+int logging_exit(void *data);
+time_t logging_get_time(int clk_id);
+long logging_get_time_ms(void);
+int logging_module_init(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval);
+int logging_module_init_with_db_path(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval,
+ const char *db_path);
+int logging_module_exit(void);
+int logging_register_listener(char *name, logging_listener_cb listener);
+int logging_unregister_listener(char *name, logging_listener_cb listener);
+int logging_get_latest_in_cache(char *name, char *appid, char **data);
+int logging_write(char *name, char *appid, char *pkgid, time_t time, char *data);
+int logging_delete(char *name, char *data);
+int logging_read_foreach(char *name, char *appid, char *pkgid,
+ time_t start_time, time_t end_time, logging_info_cb callback, void *user_data);
+void logging_update(int force);
+void logging_save_to_storage(int force);
+int logging_leveldb_put(char *key, unsigned int key_len, char *value, unsigned int value_len);
+int logging_leveldb_putv(char *key, unsigned int key_len, const char *fmt, ... );
+int logging_leveldb_read(char *key, unsigned int key_len, char *value, unsigned int value_len);
+int logging_leveldb_delete(char *key, unsigned int key_len);
+
+#endif /*__LOGGING_H__*/
--- /dev/null
+/*
+ * resourced
+ *
+ * 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.
+ * 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 logging.c
+ *
+ * @desc start logging system for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <leveldb/c.h>
+#include <Ecore.h>
+#include <sqlite3.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <assert.h>
+
+#include "trace.h"
+#include "heart.h"
+#include "logging.h"
+#include "resourced.h"
+#include "macro.h"
+#include "module.h"
+#include "config-parser.h"
+#include "notifier.h"
+
+
+#define LOGGING_BUF_MAX 1024
+#define LOGGING_PTIORITY 20
+
+#define DB_SIZE_THRESHOLD (50<<20)
+#define LOGGING_FILE_PATH HEART_FILE_PATH
+#define CREATE_QUERY "CREATE TABLE IF NOT EXISTS %s (appid TEXT, pkgid TEXT, time INT, data TEXT, idx INT, PRIMARY KEY(time, idx));"
+#define DELETE_QUERY_WITH_TIME "DELETE from %s where time < %d;"
+#define DELETE_QUERY_WITH_DATA "DELETE from %s where data = ?;"
+#define INSERT_QUERY "INSERT INTO %s values (?, ?, ?, ?, ?);"
+#define SELECT_QUERY "SELECT * FROM %s WHERE time > %d AND time < %d;"
+
+#define SELECT_BEGIN_QUERY "SELECT * FROM %s "
+#define SELECT_WHERE_QUERY "WHERE"
+#define SELECT_AND_QUERY " AND"
+#define SELECT_APPID_QUERY " appid = \'%s\'"
+#define SELECT_PKGID_QUERY " pkgid = \'%s\'"
+#define SELECT_START_QUERY " time > %d"
+#define SELECT_END_QUERY " time < %d"
+#define SELECT_FINISH_QUERY ";"
+
+#define FINALIZE_AND_RETURN_IF(cond, func, format, arg...) do \
+ { if (CONDITION(cond)) { \
+ func(format, ##arg); \
+ pthread_mutex_unlock(&(module->cache_mutex)); \
+ sqlite3_finalize(insert_stmt); \
+ sqlite3_finalize(delete_stmt); \
+ return; \
+ } } while (0)
+
+enum { read_until_null = -1 };
+
+struct logging_module {
+ char *name;
+ char *db_path;
+ sqlite3 *db;
+ enum logging_period max_period;
+ pthread_mutex_t cache_mutex;
+ logging_info_cb func;
+ time_t latest_update_time;
+ int saved_interval;
+ int updated_interval;
+ enum logging_interval save_interval;
+ enum logging_interval update_interval;
+ GQueue *cache;
+ GSList *listener;
+};
+
+struct logging_search {
+ char *appid;
+ char *pkgid;
+ time_t start;
+ time_t end;
+ void *user_data;
+ logging_info_cb func;
+};
+
+struct logging_listerner {
+ void (*func)(char *data);
+};
+
+static const struct module_ops logging_modules_ops;
+
+static pthread_t logging_data_thread = 0;
+static pthread_mutex_t logging_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t logging_data_cond = PTHREAD_COND_INITIALIZER;
+static Ecore_Timer *logging_data_timer = NULL;
+
+static pthread_t logging_update_thread = 0;
+static pthread_mutex_t logging_update_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t logging_update_cond = PTHREAD_COND_INITIALIZER;
+static Ecore_Timer *logging_update_timer = NULL;
+
+static GArray *logging_modules;
+static sqlite3 *logging_db;
+static leveldb_t *logging_leveldb;
+static leveldb_options_t *options;
+static leveldb_readoptions_t *roptions;
+static leveldb_writeoptions_t *woptions;
+
+static struct logging_object *logging_instance = NULL;
+
+time_t logging_get_time(int clk_id)
+{
+ struct timespec ts;
+
+ if (clock_gettime(clk_id, &ts) == -1) {
+ _E("clock_gettime failed");
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ }
+
+ return ts.tv_sec;
+}
+
+long logging_get_time_ms(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+}
+
+static struct logging_module *logging_find_module(char *name)
+{
+ int i;
+
+ if (!logging_modules)
+ return NULL;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ struct logging_module *module = g_array_index(logging_modules,
+ struct logging_module *, i);
+ if (!strcmp(name, module->name)) {
+ return module;
+ }
+ }
+
+ return NULL;
+}
+
+static int logging_db_busy(void * UNUSED user, int attempts)
+{
+ struct timespec req, rem;
+
+ _E("DB locked by another process, attempts number %d", attempts);
+
+ req.tv_sec = 0;
+ req.tv_nsec = 500000000;
+ nanosleep(&req, &rem);
+ return 1;
+}
+
+int logging_module_init_with_db_path(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval, const char *db_path)
+{
+ int ret;
+ sqlite3 *db = NULL;
+ const char *path = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char buf[LOGGING_BUF_MAX] = {0, };
+ struct logging_module *module;
+
+ if (!logging_instance) {
+ logging_instance = (struct logging_object *)malloc(sizeof(struct logging_object));
+ if (!logging_instance) {
+ _E("Failed to malloc logging_instance");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ logging_instance->ref = 0;
+ logging_init(NULL);
+ }
+
+ logging_instance->ref++;
+
+ /* check*/
+ if (logging_find_module(name)) {
+ _E("%s is already exist", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (db_path) {
+ /* DB create */
+ if (sqlite3_open(db_path, &db) != SQLITE_OK) {
+ _E("%s DB open failed (%s)", db_path, sqlite3_errmsg(db));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = sqlite3_exec(db, "PRAGMA locking_mode = NORMAL", 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(db));
+ _E("Skip set busy handler.");
+ } else {
+ /* Set how many times we'll repeat our attempts for sqlite_step */
+ if (sqlite3_busy_handler(db, logging_db_busy, NULL) != SQLITE_OK) {
+ _E("Couldn't set busy handler!");
+ }
+ }
+
+ path = db_path;
+ } else {
+ db = logging_db;
+ path = LOGGING_DB_FILE_NAME;
+ }
+
+ /* create table using module name and field_forms */
+ snprintf(buf, LOGGING_BUF_MAX, CREATE_QUERY, name);
+ ret = sqlite3_prepare_v2(db, buf, read_until_null, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("create %s table failed %s", name, sqlite3_errmsg(db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ _E("create %s table failed %s", name, sqlite3_errmsg(db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ sqlite3_finalize(stmt);
+
+ module = malloc(sizeof(struct logging_module));
+
+ if (!module) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* make logging_module_inform and set module_inform */
+ module->db = db;
+ module->func = func;
+ module->latest_update_time = time(NULL);
+ module->save_interval = save_interval;
+ module->saved_interval = save_interval;
+ module->update_interval = update_interval;
+ module->updated_interval = update_interval;
+ module->listener = NULL;
+
+ if (asprintf(&(module->name), "%s", name) < 0) {
+ _E("asprintf failed");
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (asprintf(&(module->db_path), "%s", path) < 0) {
+ _E("asprintf failed");
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ module->max_period = max_period;
+
+ if (pthread_mutex_init(&module->cache_mutex, NULL) < 0) {
+ _E("%s module mutex_init failed %d", name, errno);
+ free(module->name);
+ free(module);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ module->cache = g_queue_new();
+
+ if (!module->cache) {
+ _E("g_queue_new failed");
+ free(module->name);
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ g_queue_init(module->cache);
+
+ g_array_append_val(logging_modules, module);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_module_init(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval)
+{
+ return logging_module_init_with_db_path(name, max_period, save_interval, func, update_interval, NULL);
+}
+
+int logging_module_exit(void)
+{
+ if (!logging_instance)
+ return RESOURCED_ERROR_NONE;
+
+ logging_instance->ref--;
+
+ if (logging_instance->ref == 0) {
+ free(logging_instance);
+ logging_instance = NULL;
+ logging_exit(NULL);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_register_listener(char *name, logging_listener_cb listener_cb)
+{
+ GSList *registerd;
+ struct logging_listerner *listener;
+ struct logging_module *module;
+
+ if (!listener_cb) {
+ _E("invalid listern func");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ registerd = g_slist_find(module->listener, listener_cb);
+
+ if (registerd) {
+ _E("already registerd listener %x", listener_cb);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ listener = malloc(sizeof(struct logging_listerner));
+ if (!listener) {
+ _E("Fail to malloc for notifier!");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ listener->func = listener_cb;
+
+ module->listener = g_slist_append(module->listener, listener);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_unregister_listener(char *name, logging_listener_cb listener_cb)
+{
+ GSList *registerd;
+ struct logging_module *module;
+
+ if (!listener_cb) {
+ _E("invalid listern func");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ registerd = g_slist_find(module->listener, listener_cb);
+
+ if (!registerd) {
+ _E("It is not registered listerner %x", listener_cb);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module->listener = g_slist_remove(module->listener, registerd->data);
+
+ free(registerd->data);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_operate(char *name, char *appid, char *pkgid, time_t time, char *data, int operation)
+{
+ /* Save data to cache */
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ table = malloc(sizeof(struct logging_table_form));
+
+ if (!table) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (operation == INSERT) {
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", appid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", pkgid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ table->time = time;
+
+ if (asprintf(&(table->data), "%s", data) < 0) {
+ _E("asprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ table->operation = operation;
+
+ pthread_mutex_lock(&(module->cache_mutex));
+ g_queue_push_tail(module->cache, (gpointer)table);
+ pthread_mutex_unlock(&(module->cache_mutex));
+
+ /* call listners */
+ if (module->listener) {
+ GSList *iter;
+ struct logging_listerner *listener;
+ char buf[LOGGING_BUF_MAX];
+
+ gslist_for_each_item(iter, module->listener) {
+ listener = (struct logging_listerner *)iter->data;
+ snprintf(buf, LOGGING_BUF_MAX, "%s %s %d %s", appid, pkgid, (int)time, data);
+
+ listener->func(buf);
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_write(char *name, char *appid, char *pkgid, time_t time, char *data)
+{
+ return logging_operate(name, appid, pkgid, time, data, INSERT);
+}
+
+int logging_delete(char *name, char *data)
+{
+ time_t time = 0;
+ return logging_operate(name, NULL, NULL, time, data, DELETE);
+}
+
+int logging_leveldb_put(char *key, unsigned int key_len, char *value, unsigned int value_len)
+{
+ char *err = NULL;
+
+ if (!key || !key_len || !value || !value_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_put(logging_leveldb, woptions, key, key_len, value, value_len, &err);
+ if (err != NULL) {
+ _E("Failed to put to leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_leveldb_putv(char *key, unsigned int key_len, const char *fmt, ...)
+{
+ char *err = NULL;
+ va_list ap;
+ char value[LOGGING_BUF_MAX];
+ unsigned int value_len;
+
+ if (!key || !key_len || !fmt)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(value, LOGGING_BUF_MAX, fmt, ap);
+ va_end(ap);
+
+ value_len = strlen(value);
+ if (!value_len) {
+ _E("Failed to get length of string");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_put(logging_leveldb, woptions, key, key_len, value, value_len, &err);
+ if (err != NULL) {
+ _E("Failed to put to leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_leveldb_read(char *key, unsigned int key_len, char *value, unsigned int value_len)
+{
+ unsigned int read_len;
+ char *err = NULL;
+ char *result = NULL;
+
+ if (!key || !key_len || !value || !value_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ result = leveldb_get(logging_leveldb, roptions, key, key_len, &read_len, &err);
+ if (err != NULL) {
+ _E("Failed to get from leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ if (value_len < read_len)
+ snprintf(value, value_len, "%s", result);
+ else
+ snprintf(value, read_len + 1, "%s", result);
+
+ free(result);
+
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_leveldb_delete(char *key, unsigned int key_len)
+{
+ char *err = NULL;
+
+ if (!key || !key_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_delete(logging_leveldb, woptions, key, key_len, &err);
+ if (err != NULL) {
+ _E("Failed to delete from leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_get_latest_in_cache(char *name, char *appid, char **data)
+{
+ int i, len;
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!appid) {
+ _E("appid parameter should be valid");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ pthread_mutex_lock(&(module->cache_mutex));
+ len = g_queue_get_length(module->cache);
+ if (!len) {
+ _I("%s cache is empty", module->name);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ *data = NULL;
+
+ for (i = 0; i < len; i++) {
+ table = g_queue_peek_nth(module->cache, i);
+
+ if (table &&
+ !strcmp(appid, table->appid))
+ *data = table->data;
+ }
+ pthread_mutex_unlock(&(module->cache_mutex));
+
+ if (!*data) {
+ _E("NOT found in cache %s", appid);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void logging_cache_search(struct logging_table_form *data, struct logging_search *search)
+{
+ /* search in cache */
+ /* true condition, call function */
+ if (search->appid) {
+ if (strcmp(search->appid, data->appid))
+ return;
+
+ if (search->start && search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->pkgid) {
+ if (strcmp(search->pkgid, data->pkgid))
+ return;
+
+ if (search->start && search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->start) {
+ if (search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->end) {
+ if (search->end > data->time)
+ return;
+ }
+
+ search->func(data, search->user_data);
+}
+
+int logging_read_foreach(char *name, char *appid, char *pkgid,
+ time_t start_time, time_t end_time, logging_info_cb callback, void *user_data)
+{
+ /* Read from storage (cache & db) */
+ int result;
+ int len;
+ time_t cur_time;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_table_form table;
+ struct logging_module *module;
+ struct logging_search search;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ cur_time = time(NULL);
+ search.appid = NULL;
+ search.pkgid = NULL;
+ search.start = 0;
+ search.end = cur_time;
+ search.func = callback;
+ search.user_data = user_data;
+
+ len = snprintf(buf, LOGGING_BUF_MAX, SELECT_BEGIN_QUERY, name);
+
+ if (appid) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.appid = appid;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_APPID_QUERY, appid);
+
+ if (start_time) {
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ } else if (pkgid) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.pkgid = pkgid;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_PKGID_QUERY, pkgid);
+
+ if (start_time) {
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ } else if (start_time) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ if (end_time && cur_time > end_time)
+ search.end = end_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_END_QUERY, (int)search.end);
+
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_FINISH_QUERY);
+
+ /* search from db */
+ if (sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL) != SQLITE_OK) {
+ _E("select failed");
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ do {
+ result = sqlite3_step(stmt);
+ switch (result) {
+ case SQLITE_ROW:
+ snprintf(table.appid, MAX_APPID_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 0));
+ snprintf(table.pkgid, MAX_PKGNAME_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 1));
+ table.time = sqlite3_column_int(stmt, 2);
+ if (module->latest_update_time < table.time)
+ module->latest_update_time = table.time;
+ table.data = (char *)sqlite3_column_text(stmt, 3);
+
+ callback(&table, user_data);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ _E("select %s table failed %s", name, sqlite3_errmsg(module->db));
+ /* FALLTHROUGH */
+ default:
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ } while (result == SQLITE_ROW);
+
+ sqlite3_finalize(stmt);
+
+ /* search from cache */
+ if (!g_queue_is_empty(module->cache)) {
+ pthread_mutex_lock(&(module->cache_mutex));
+ g_queue_foreach(module->cache, (GFunc)logging_cache_search, (gpointer)&search);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int logging_reset(char *name)
+{
+ /* Table cut using max period */
+ time_t curr_time, del_time;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_module *module;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ time(&curr_time);
+
+ switch (module->max_period) {
+ case ONE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(1);
+ break;
+ case THREE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(3);
+ break;
+ case SIX_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(6);
+ break;
+ case TWELVE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(12);
+ break;
+ case ONE_DAY:
+ del_time = curr_time - HOUR_TO_SEC(24);
+ break;
+ case ONE_WEEK:
+ del_time = curr_time - DAY_TO_SEC(7);
+ break;
+ case ONE_MONTH:
+ del_time = curr_time - MONTH_TO_SEC(1);
+ break;
+ case FOUR_MONTH:
+ del_time = curr_time - MONTH_TO_SEC(4);
+ break;
+ default:
+ _E("%s invalid max period", module->name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ snprintf(buf, LOGGING_BUF_MAX, DELETE_QUERY_WITH_TIME, name, (int)del_time);
+
+ if (sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL) != SQLITE_OK) {
+ _E("delete %s table failed %s", name, sqlite3_errmsg(module->db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ _E("delete %s table failed %s", name, sqlite3_errmsg(module->db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ sqlite3_finalize(stmt);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int logging_check_storage_size(const char *db_path)
+{
+ int ret;
+ struct stat db_stat = {0};
+
+ if (!db_path)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ ret = stat(db_path, &db_stat);
+
+ if (ret) {
+ _E("Failed to get statistics for %s errno %d",
+ db_path, errno);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (db_stat.st_size >= DB_SIZE_THRESHOLD)
+ return RESOURCED_ERROR_FAIL;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+void logging_update(int force)
+{
+ int i, ret;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_table_form table;
+ struct logging_module *module;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ if (!logging_modules)
+ return;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules,
+ struct logging_module *, i);
+
+ /* no update when module update func is not registered */
+ if (module->func == NULL)
+ continue;
+
+ if (!force && module->updated_interval > 0) {
+ module->updated_interval -= ONE_MINUTE;
+ continue;
+ }
+
+ module->updated_interval = module->update_interval;
+
+ snprintf(buf, LOGGING_BUF_MAX, SELECT_QUERY, module->name,
+ (int)module->latest_update_time, (int)time(NULL));
+
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("select failed");
+ sqlite3_finalize(stmt);
+ return;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ switch (ret) {
+ case SQLITE_ROW:
+ snprintf(table.appid, MAX_APPID_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 0));
+ snprintf(table.pkgid, MAX_PKGNAME_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 1));
+ table.time = sqlite3_column_int(stmt, 2);
+ if (module->latest_update_time < table.time)
+ module->latest_update_time = table.time;
+ if (asprintf(&(table.data), "%s", (char *)sqlite3_column_text(stmt, 3)) < 0) {
+ _E("asprintf failed");
+ sqlite3_finalize(stmt);
+ return;
+ }
+ module->func(&table, NULL);
+ free(table.data);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ _E("select %s table failed %s", module->name,
+ sqlite3_errmsg(module->db));
+ /* FALLTHROUGH */
+ default:
+ sqlite3_finalize(stmt);
+ return;
+ }
+ } while (ret == SQLITE_ROW);
+
+ sqlite3_finalize(stmt);
+ }
+
+}
+
+void logging_save_to_storage(int force)
+{
+ /* Save cache to storage */
+ static int index = 0;
+ int i, j, len, ret = 0;
+ sqlite3_stmt *insert_stmt = NULL;
+ sqlite3_stmt *delete_stmt = NULL;
+ char buf[LOGGING_BUF_MAX] = {0, };
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ if (!logging_modules)
+ return;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules, struct logging_module *, i);
+
+ if (!force && module->saved_interval > 0) {
+ module->saved_interval -= ONE_MINUTE;
+ continue;
+ }
+
+ module->saved_interval = module->save_interval;
+
+ /* find q and pop */
+ pthread_mutex_lock(&(module->cache_mutex));
+ len = g_queue_get_length(module->cache);
+ if (!len) {
+ _I("%s cache is empty", module->name);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ continue;
+ }
+
+ sqlite3_exec(module->db, "BEGIN;", NULL, NULL, NULL);
+
+ snprintf(buf, LOGGING_BUF_MAX, INSERT_QUERY, module->name);
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &insert_stmt, NULL);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ snprintf(buf, LOGGING_BUF_MAX, DELETE_QUERY_WITH_DATA, module->name);
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &delete_stmt, NULL);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ for (j = 0; j < len; j++) {
+ table = g_queue_peek_head(module->cache);
+ if (!table)
+ continue;
+
+ if (table->operation == DELETE) {
+ sqlite3_reset(delete_stmt);
+
+ ret = sqlite3_bind_text(delete_stmt, 1, table->data, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind data : %s for preparing statement", table->pkgid);
+
+ ret = sqlite3_step(delete_stmt);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_DONE, _E, "delete %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ } else {
+ /* else if (table->operation == INSERT) */
+ sqlite3_reset(insert_stmt);
+
+ ret = sqlite3_bind_text(insert_stmt, 1, table->appid, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind appid : %s for preparing statement", table->appid);
+
+ ret = sqlite3_bind_text(insert_stmt, 2, table->pkgid, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind pkgid : %s for preparing statement", table->pkgid);
+
+ ret = sqlite3_bind_int(insert_stmt, 3, table->time);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind time : %d for preparing statement", table->time);
+
+ ret = sqlite3_bind_text(insert_stmt, 4, table->data, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind data : %s for preparing statement", table->data);
+
+ ret = sqlite3_bind_int(insert_stmt, 5, index++);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind index : %d for preparing statement", index);
+
+ ret = sqlite3_step(insert_stmt);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_DONE, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+ }
+
+ table = g_queue_pop_head(module->cache);
+ free(table);
+ }
+ pthread_mutex_unlock(&(module->cache_mutex));
+ sqlite3_exec(module->db, "COMMIT;", NULL, NULL, NULL);
+ sqlite3_finalize(insert_stmt);
+ insert_stmt = NULL;
+ sqlite3_finalize(delete_stmt);
+ delete_stmt = NULL;
+ }
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules, struct logging_module *, i);
+
+ /* Check storage limitation by maximum period and storage size (50MiB) */
+ if (logging_check_storage_size(module->db_path) == RESOURCED_ERROR_FAIL) {
+ logging_reset(module->name);
+ sqlite3_exec(module->db, "VACUUM;", NULL, NULL, NULL);
+ }
+ }
+}
+
+static void *logging_data_thread_main(void *arg)
+{
+ int ret = 0;
+
+ setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
+
+ while (1) {
+ /*
+ * When signalled by main thread,
+ * it starts logging_pthread().
+ */
+ ret = pthread_mutex_lock(&logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&logging_data_cond, &logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&logging_data_mutex);
+ if (ret)
+ _E("logging data thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ logging_save_to_storage(false);
+
+ ret = pthread_mutex_unlock(&logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* Now our thread finishes - cleanup TID */
+ logging_data_thread = 0;
+
+ return NULL;
+}
+
+static Eina_Bool logging_send_signal_to_data(void *data)
+{
+ int ret;
+
+ _D("logging timer callback function start");
+
+ /* signal to logging data thread for start logging */
+ ret = pthread_mutex_trylock(&logging_data_mutex);
+
+ if (ret)
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ else {
+ pthread_cond_signal(&logging_data_cond);
+ _I("send signal to logging data thread");
+ pthread_mutex_unlock(&logging_data_mutex);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static int logging_start(void *data)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+static void *logging_update_thread_main(void *arg)
+{
+ int ret = 0;
+
+ setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of registered module.
+ */
+ ret = pthread_mutex_lock(&logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&logging_update_cond, &logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&logging_update_mutex);
+ if (ret)
+ _E("logging update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ logging_update(false);
+
+ ret = pthread_mutex_unlock(&logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* Now our thread finishes - cleanup TID */
+ logging_update_thread = 0;
+
+ return NULL;
+}
+
+static Eina_Bool logging_send_signal_to_update(void *data)
+{
+ int ret;
+ DIR *dir_info;
+
+ dir_info = opendir(LOGGING_FILE_PATH);
+
+ if (dir_info)
+ closedir(dir_info);
+ else {
+ _E("There is no %s", LOGGING_FILE_PATH);
+ ret = mkdir(LOGGING_FILE_PATH, S_IRWXU | S_IRWXG | S_IROTH);
+
+ if (ret) {
+ _E("mkdir failed %s", LOGGING_FILE_PATH);
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+
+ _D("logging timer callback function start");
+
+ /* signal to logging update thread for start update */
+ ret = pthread_mutex_trylock(&logging_update_mutex);
+
+ if (ret)
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ else {
+ pthread_cond_signal(&logging_update_cond);
+ _I("send signal to logging update thread");
+ pthread_mutex_unlock(&logging_update_mutex);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static int logging_thread_create(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ /* initialize logging_data_thread */
+ if (logging_data_thread) {
+ _I("logging data thread %u already created", (unsigned)logging_data_thread);
+ } else {
+ ret = pthread_create(&logging_data_thread, NULL,
+ (void *)logging_data_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for logging_data_thread_main failed, %d\n", ret);
+ logging_data_thread = 0;
+ } else {
+ _D("pthread creation for logging data success");
+ pthread_detach(logging_data_thread);
+ }
+ }
+
+ /* initialize logging_update_thread */
+ if (logging_update_thread) {
+ _I("logging update thread %u already created", (unsigned)logging_update_thread);
+ } else {
+ ret = pthread_create(&logging_update_thread, NULL,
+ (void *)logging_update_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for logging_update_thread_main failed, %d\n", ret);
+ logging_update_thread = 0;
+ } else {
+ _D("pthread creation for logging update success");
+ pthread_detach(logging_update_thread);
+ }
+ }
+ return ret;
+}
+
+static int logging_poweroff(void *data)
+{
+ /* flush module cache */
+ logging_save_to_storage(true);
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_init(void *data)
+{
+ int ret = RESOURCED_ERROR_NONE;
+ char *err = NULL;
+
+ _D("logging_init start");
+
+ ret = logging_thread_create();
+ if (ret) {
+ _E("logging thread create failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* module array create */
+ logging_modules = g_array_new(false, false, sizeof(struct logging_module *));
+
+ if (logging_modules == NULL) {
+ _E("logging_modules_array create failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* DB create */
+ if (sqlite3_open(LOGGING_DB_FILE_NAME, &logging_db) != SQLITE_OK) {
+ _E("%s DB open failed (%s)", LOGGING_DB_FILE_NAME, sqlite3_errmsg(logging_db));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = sqlite3_exec(logging_db, "PRAGMA locking_mode = NORMAL", 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(logging_db));
+ _E("Skip set busy handler.");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ /* Set how many times we'll repeat our attempts for sqlite_step */
+ if (sqlite3_busy_handler(logging_db, logging_db_busy, NULL) != SQLITE_OK) {
+ _E("Couldn't set busy handler!");
+ }
+
+ options = leveldb_options_create();
+ leveldb_options_set_create_if_missing(options, 1);
+ logging_leveldb = leveldb_open(options, LOGGING_LEVEL_DB_FILE_NAME, &err);
+ if (err != NULL) {
+ _E("Failed to open leveldb");
+ free(err);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ roptions = leveldb_readoptions_create();
+ woptions = leveldb_writeoptions_create();
+ leveldb_writeoptions_set_sync(woptions, 1);
+
+ register_notifier(RESOURCED_NOTIFIER_LOGGING_START, logging_start);
+ register_notifier(RESOURCED_NOTIFIER_POWER_OFF, logging_poweroff);
+
+ if (logging_data_timer == NULL) {
+ _D("logging data timer start");
+ logging_data_timer =
+ ecore_timer_add(ONE_MINUTE, logging_send_signal_to_data, (void *)NULL);
+ }
+
+ if (logging_update_timer == NULL) {
+ _D("logging data timer start");
+ logging_update_timer =
+ ecore_timer_add(ONE_MINUTE, logging_send_signal_to_update, (void *)NULL);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int logging_exit(void *data)
+{
+ int i;
+ struct logging_module *module;
+
+ /* update timer delete */
+ ecore_timer_del(logging_update_timer);
+ logging_update_timer = NULL;
+
+ unregister_notifier(RESOURCED_NOTIFIER_LOGGING_START, logging_start);
+ unregister_notifier(RESOURCED_NOTIFIER_POWER_OFF, logging_poweroff);
+
+ /* flush module cache */
+ logging_save_to_storage(true);
+
+ /* logging_modules array deinitialize */
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules,
+ struct logging_module *, i);
+ free(module->name);
+ free(module->db_path);
+ g_queue_free(module->cache);
+
+ /* DB close */
+ sqlite3_close(module->db);
+ }
+
+ g_array_free(logging_modules, true);
+
+ /* DB close */
+ sqlite3_close(logging_db);
+ if (logging_leveldb)
+ leveldb_close(logging_leveldb);
+ _D("logging_exit");
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+PROJECT(mem-stress)
+
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/InstallSymlink.cmake)
+
+ADD_EXECUTABLE(${PROJECT_NAME}
+ ${CMAKE_CURRENT_SOURCE_DIR}/mem-stress.c
+ )
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} resourced_shared)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mem-stress.service
+ DESTINATION lib/systemd/system)
+
+INSTALL_SYMLINK(../mem-stress.service lib/systemd/system/graphical.target.wants/mem-stress.service)
--- /dev/null
+/*
+ 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.
+
+ 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
+*/
+
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "util.h"
+#include "config-parser.h"
+
+#define MEM_STRESS_CONF "/etc/mem-stress.conf"
+
+static bool quite = false;
+static size_t arg_size = 0;
+
+static int parse_config_file(void)
+{
+ const ConfigTableItem items[] = {
+ { "MemStress", "Size", config_parse_bytes, 0, &arg_size },
+ { NULL, NULL, NULL, 0, NULL }
+ };
+
+ return config_parse_new(MEM_STRESS_CONF, (void*) items);
+}
+
+static void mem_stress_signal_handler(int signo)
+{
+ if (signo == SIGTERM)
+ quite = true;
+}
+
+static int mem_stress_signal_init(void)
+{
+ if (signal(SIGTERM, &mem_stress_signal_handler) == SIG_ERR) {
+ fprintf(stdout, "Failed to catch SIGTERM signal: %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int mem_stress_allocate_memory(void **addr, size_t len)
+{
+ void *p = NULL;
+ int r;
+
+ do {
+ fprintf(stdout, "Try to allocate memory %u", len);
+ p = new0(void, len);
+ } while(!p);
+
+ r = mlock(p, len);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ *addr = p;
+
+ return 0;
+}
+
+static int mem_stress_free_memory(void *addr, size_t len)
+{
+ int r;
+
+ r = munlock(addr, len);
+ if (r < 0)
+ return r;
+
+ free(addr);
+
+ return 0;
+}
+
+static int mem_stress_run_loop(void)
+{
+ void *mem = NULL;
+ int r;
+ char buf[256];
+
+ fprintf(stdout, "Memory stress size is: %u", arg_size);
+
+ if (!arg_size)
+ return 0;
+
+ r = mem_stress_allocate_memory(&mem, arg_size);
+ if (r < 0) {
+ fprintf(stderr, "Failed to allocate memory: %s", strerror_r(-r, buf, sizeof(buf)));
+ return r;
+ }
+
+ while(!quite)
+ sleep(10);
+
+ r = mem_stress_free_memory(mem, arg_size);
+ if (r < 0) {
+ fprintf(stderr, "Failed to free memory: %s", strerror_r(-r, buf, sizeof(buf)));
+ return r;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+
+ r = access(MEM_STRESS_CONF, F_OK);
+ if (r < 0) {
+ fprintf(stderr, "Failed to access '%s': %m", MEM_STRESS_CONF);
+ return EXIT_FAILURE;
+ }
+
+ r = mem_stress_signal_init();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = parse_config_file();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = mem_stress_run_loop();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+[Unit]
+Description=Memory Stress
+After=graphical.target
+ConditionPathExists=/etc/mem-stress.conf
+
+[Service]
+ExecStart=/usr/bin/mem-stress
+CPUSchedulingPolicy=idle
+OOMScoreAdjust=-1000
+StandardOutput=journal
+StandardError=inherit
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2012 - 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 lowmem_dbus.c
+ *
+ * @desc lowmem dbus for oom triger
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <Ecore.h>
+
+#include "trace.h"
+#include "lowmem-handler.h"
+#include "edbus-handler.h"
+#include "resourced.h"
+#include "macro.h"
+#include "memory-common.h"
+#include "procfs.h"
+
+static void lowmem_dbus_oom_set_threshold(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ int level, thres;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_THRESHOLD);
+
+ if (ret == 0) {
+ _D("there is no oom set threshold signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &level,
+ DBUS_TYPE_INT32, &thres, DBUS_TYPE_INVALID);
+
+ if (ret == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, level, thres);
+}
+
+static void lowmem_dbus_oom_set_leave_threshold(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ int thres;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_LEAVE_THRESHOLD);
+
+ if (ret == 0) {
+ _D("there is no oom set leave threshold signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &thres,
+ DBUS_TYPE_INVALID);
+
+ if (ret == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, thres);
+}
+
+static void lowmem_dbus_oom_trigger(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_TRIGGER);
+ if (ret == 0) {
+ _D("there is no oom trigger signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ lowmem_change_memory_state(LOWMEM_LOW, 1);
+ lowmem_memory_oom_killer(OOM_FORCE | OOM_NOMEMORY_CHECK);
+ lowmem_change_memory_state(LOWMEM_NORMAL, 0);
+}
+
+static void lowmem_dbus_set_perceptible(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ pid_t pid;
+
+ dbus_error_init(&err);
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_PERCEPTIBLE);
+ if (ret == 0) {
+ _D("there is no set perceptible signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+ proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_PERCEPTIBLE);
+}
+
+static void lowmem_dbus_set_platform(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ pid_t pid;
+
+ dbus_error_init(&err);
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_PLATFORM);
+ if (ret == 0) {
+ _D("there is no set platform swap signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+ lowmem_trigger_swap(pid, MEMCG_PLATFORM);
+}
+
+static const struct edbus_signal edbus_signals[] = {
+ /* RESOURCED DBUS */
+ {RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_THRESHOLD, lowmem_dbus_oom_set_threshold, NULL},
+ {RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_LEAVE_THRESHOLD,
+ lowmem_dbus_oom_set_leave_threshold, NULL},
+ {RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_TRIGGER, lowmem_dbus_oom_trigger, NULL},
+ {RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_PERCEPTIBLE, lowmem_dbus_set_perceptible, NULL},
+ {RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+ SIGNAL_OOM_SET_PLATFORM, lowmem_dbus_set_platform, NULL},
+};
+
+void lowmem_dbus_init(void)
+{
+ edbus_add_signals(edbus_signals, ARRAY_SIZE(edbus_signals));
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 lowmem_handler.h
+ * @desc handler function for setting memcgroup memory controller and
+ * receiving event fd.
+ **/
+
+#ifndef __LOWMEM_HANDLER_H__
+#define __LOWMEM_HANDLER_H__
+
+#include <memory-common.h>
+
+void lowmem_dbus_init(void);
+int lowmem_memory_oom_killer(int flags);
+int lowmem_proactive_oom_killer(int flags, char *appid);
+void lowmem_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);
+unsigned long lowmem_get_ktotalram(void);
+void lowmem_trigger_swap(pid_t pid, int memcg_idx);
+
+/*
+ * Return memcg pointer to selected cgroup.
+ */
+int lowmem_get_memcg(enum memcg_type type, struct memcg **memcg_ptr);
+
+enum oom_killer_cb_flags {
+ OOM_NONE = 0x00000000, /* for main oom killer thread */
+ OOM_FORCE = 0x00000001, /* for forced kill */
+ OOM_TIMER_CHECK = 0x00000002, /* for timer oom killer cb */
+ OOM_NOMEMORY_CHECK = 0x00000004, /* check victims' memory */
+};
+
+#endif /*__LOWMEM_HANDLER_H__*/
--- /dev/null
+/*
+ * 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 "memory-common.h"
+#include "cgroup.h"
+
+#include <stdlib.h>
+
+#define BUF_MAX 1024
+
+void memcg_info_set_limit(struct memcg_info *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 *memcg_info_alloc_subcgroup(struct memcg *memcg)
+{
+ struct memcg_info *pmi, *mi;
+ int i;
+ if (!memcg)
+ return NULL;
+ mi = (struct memcg_info *)malloc(sizeof(struct memcg_info));
+ 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,
+ sizeof(mi->event_level)-1);
+ mi->event_level[sizeof(mi->event_level)-1] = 0;
+ mi->evfd = pmi->evfd;
+ return mi;
+}
+
+static int memcg_add_cgroup(struct memcg *memcg)
+{
+ struct memcg_info *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 *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 *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 *memcg)
+{
+ GSList *iter = NULL;
+ memcg_info_show(memcg->info);
+ gslist_for_each_item(iter, memcg->cgroups) {
+ struct memcg_info *mi =
+ (struct memcg_info *)(iter->data);
+ memcg_info_show(mi);
+ }
+}
+
+void memcg_info_init(struct memcg_info *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,
+ sizeof(mi->event_level)-1);
+ mi->event_level[sizeof(mi->event_level)-1] = 0;
+ strncpy(mi->name, name, sizeof(mi->name)-1);
+ mi->name[sizeof(mi->name)-1] = 0;
+}
+
+void memcg_init(struct memcg *memcg)
+{
+ memcg->num_subcgroup = MEMCG_DEFAULT_NUM_SUBCGROUP;
+ memcg->use_hierarchy = MEMCG_DEFAULT_USE_HIERARCHY;
+ memcg->info = NULL;
+ memcg->cgroups = NULL;
+}
+
+int memcg_get_anon_usage(struct memcg_info *mi, unsigned int *anon_usage)
+{
+ FILE *f;
+ char buf[BUF_MAX] = {0,};
+ char line[BUF_MAX] = {0, };
+ char name[BUF_MAX] = {0, };
+ unsigned int tmp, active_anon = 0, inactive_anon = 0;
+
+ snprintf(buf, sizeof(buf), "%smemory.stat", mi->name);
+ _I("get mem usage anon from %s", buf);
+
+ f = fopen(buf, "r");
+ if (!f) {
+ _E("%s open failed, %d", buf, f);
+ return RESOURCED_ERROR_FAIL;
+ }
+ while (fgets(line, BUF_MAX, f) != NULL) {
+ if (sscanf(line, "%s %d", name, &tmp)) {
+ if (!strcmp(name, "inactive_anon")) {
+ inactive_anon = tmp;
+ } else if (!strcmp(name, "active_anon")) {
+ active_anon = tmp;
+ break;
+ }
+ }
+ }
+
+ fclose(f);
+ *anon_usage = active_anon + inactive_anon;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+int memcg_get_usage(struct memcg_info *mi, unsigned int *usage_bytes)
+{
+ return cgroup_read_node(mi->name, "memory.usage_in_bytes", usage_bytes);
+}
+
+/*
+ * Usage example:
+ * int i;
+ * pid_t pid;
+ * GArray *pids_array = g_array_new(false, false, sizeof(pid_t));
+ *
+ * memcg_get_pids(mi, pids_array);
+ *
+ * for (i=0; i < pids_array->len; i++)
+ * _D("pid_t: %d", g_array_index(pids_array, pid_t, i));
+ * g_array_free(pids_array, TRUE);
+ *
+ */
+int memcg_get_pids(struct memcg_info *mi, GArray *pids)
+{
+ FILE *f;
+ pid_t tpid;
+ char buf[MAX_PATH_LENGTH] = {0, };
+
+ if (pids == NULL)
+ return RESOURCED_ERROR_FAIL;
+
+ snprintf(buf, sizeof(buf), "%scgroup.procs", mi->name);
+
+ f = fopen(buf, "r");
+ if (!f) {
+ _E("%s open failed, %d", buf, f);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ while (fgets(buf, 32, f) != NULL) {
+ tpid = atoi(buf);
+ g_array_append_val(pids, tpid);
+ }
+ fclose(f);
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+[VIP_PROCESS]
+# predefined process list
+PREDEFINE=Xorg
+PREDEFINE=enlightenment
+PREDEFINE=dbus-daemon
+PREDEFINE=amd
+PREDEFINE=launchpad_preloading_preinitializing_daemon
+PREDEFINE=process_pool_launchpad_preloading_preinitializing_daemon
+
+[Memory64]
+# Threshold to start swap
+ThresholdSwap=15 # MB
+
+# Threshold to start reclaim
+ThresholdLow=8 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=5 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=8 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=1
+
+[Memory256]
+# Threshold to start swap
+ThresholdSwap=40 # MB
+
+# Threshold to start reclaim
+ThresholdLow=20 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=10 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=20 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=2
+
+[Memory448]
+# Threshold to start swap
+ThresholdSwap=100 # MB
+
+# Threshold to start reclaim
+ThresholdLow=60 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=50 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=70 # MB
+
+# Threshold for proactive memory killer
+ProactiveThreshold=80 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=100 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=30 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory512]
+# Threshold to start swap
+ThresholdSwap=100 # MB
+
+# Threshold to start reclaim
+ThresholdLow=70 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=60 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=80 # MB
+
+# Threshold for proactive memory killer
+ProactiveThreshold=80 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=100 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=30 # 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 proactive memory killer
+ProactiveThreshold=150 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=230 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=50 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Foreground use hierarchy
+# ForegroundUseHierarchy=1
+
+# Foreground # of cgroups
+# ForegroundNumCgroups=3
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory1024]
+# 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
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory2048]
+# Threshold to start swap
+ThresholdSwap=300 # MB
+
+# Threshold to start reclaim
+ThresholdLow=200 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=160 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=300 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=10
+
+[POPUP]
+oom_popup=no
--- /dev/null
+[VIP_PROCESS]
+# predefined process list
+PREDEFINE=Xorg
+PREDEFINE=enlightenment
+PREDEFINE=dbus-daemon
+PREDEFINE=amd
+PREDEFINE=launchpad_preloading_preinitializing_daemon
+PREDEFINE=process_pool_launchpad_preloading_preinitializing_daemon
+
+[Memory64]
+# Threshold to start swap
+ThresholdSwap=15 # MB
+
+# Threshold to start reclaim
+ThresholdLow=8 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=5 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=8 # MB
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+[Memory256]
+# Threshold to start swap
+ThresholdSwap=40 # MB
+
+# Threshold to start reclaim
+ThresholdLow=20 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=10 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=20 # MB
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+# Number of max victims
+NumMaxVictims=2
+
+[Memory448]
+# Threshold to start swap
+ThresholdSwap=100 # MB
+
+# Threshold to start reclaim
+ThresholdLow=60 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=50 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=70 # MB
+
+# Threshold for proactive memory killer
+ProactiveThreshold=80 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=100 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=30 # MB
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory512]
+# Threshold to start swap
+ThresholdSwap=100 # MB
+
+# Threshold to start reclaim
+ThresholdLow=70 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=60 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=80 # MB
+
+# Threshold for proactive memory killer
+ProactiveThreshold=80 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=100 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=30 # 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 proactive memory killer
+ProactiveThreshold=150 # MB
+
+# Leave Threshold for proactive memory killer
+ProactiveLeave=230 # MB
+
+# Threshold for dynamic memory killer
+DynamicThreshold=50 # MB
+
+# Foreground limit ratio
+ForegroundRatio=0.6
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory1024]
+# 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
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+# Number of max victims
+NumMaxVictims=5
+
+[Memory2048]
+# Threshold to start swap
+ThresholdSwap=300 # MB
+
+# Threshold to start reclaim
+ThresholdLow=200 # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=160 # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=300 # MB
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+# Number of max victims
+NumMaxVictims=10
+
+[POPUP]
+oom_popup=no
--- /dev/null
+/*
+ * resourced
+ *
+ * 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.
+ * 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 vmpressure-lowmem-handler.c
+ *
+ * @desc lowmem handler using memcgroup
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <limits.h>
+#include <vconf.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/shm.h>
+#include <sys/eventfd.h>
+#include <sys/sysinfo.h>
+#include <Ecore.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <bundle.h>
+#include <eventsystem.h>
+
+#include "freezer.h"
+#include "trace.h"
+#include "cgroup.h"
+#include "lowmem-handler.h"
+#include "proc-common.h"
+#include "procfs.h"
+#include "lowmem-common.h"
+#include "resourced.h"
+#include "macro.h"
+#include "notifier.h"
+#include "config-parser.h"
+#include "module.h"
+#include "swap-common.h"
+#include "cgroup.h"
+#include "memory-common.h"
+#include "heart-common.h"
+#include "proc-main.h"
+#include "edbus-handler.h"
+
+#define LOWMEM_DEFAULT_CGROUP "/sys/fs/cgroup/memory"
+#define LOWMEM_NO_LIMIT 0
+#define LOWMEM_THRES_INIT 0
+
+/* Experimently, RSS is 3 times larger than the actual allocated memory. */
+#define LOWMEM_RSS_RATIO 0.3
+
+#define MEMPS_LOG_PATH "/var/log/"
+#define MEMPS_LOG_FILE MEMPS_LOG_PATH"memps"
+#define MEMPS_EXEC_PATH "usr/bin/memps"
+#define MEMCG_MOVE_CHARGE_PATH "memory.move_charge_at_immigrate"
+#define MEMCG_OOM_CONTROL_PATH "memory.oom_control"
+#define MEMCG_LIMIT_PATH "memory.limit_in_bytes"
+#define MEMCG_EVENTFD_CONTROL "cgroup.event_control"
+#define MEMCG_EVENTFD_MEMORY_PRESSURE "memory.pressure_level"
+#define MEM_CONF_FILE "/etc/resourced/memory.conf"
+#define MEM_VIP_SECTION "VIP_PROCESS"
+#define MEM_VIP_PREDEFINE "PREDEFINE"
+#define MEM_POPUP_SECTION "POPUP"
+#define MEM_POPUP_STRING "oom_popup"
+
+#define BtoMB(x) ((x) >> 20)
+#define BtoKB(x) ((x) >> 10)
+#define KBtoMB(x) ((x) >> 10)
+#define BtoPAGE(x) ((x) >> 12)
+#define MBtoKB(x) ((x) << 10)
+
+#define BUF_MAX 1024
+#define MAX_MEMORY_CGROUP_VICTIMS 10
+#define MAX_CGROUP_VICTIMS 1
+#define OOM_TIMER_INTERVAL 2
+#define OOM_KILLER_PRIORITY -20
+#define MAX_FD_VICTIMS 10
+#define MAX_MEMPS_LOGS 50
+#define MAX_FGRD_KILL 3
+#define NUM_RM_LOGS 5
+#define THRESHOLD_MARGIN 10 /* MB */
+
+#define MEM_SIZE_64 64 /* MB */
+#define MEM_SIZE_256 256 /* MB */
+#define MEM_SIZE_448 448 /* 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 */
+
+/* thresholds for 64M RAM*/
+#define PROACTIVE_64_THRES 10 /* MB */
+#define PROACTIVE_64_LEAVE 30 /* MB */
+#define DYNAMIC_64_THRES 5 /* MB */
+#define MEMCG_MEMORY_64_THRES_SWAP 15 /* MB */
+#define MEMCG_MEMORY_64_THRES_LOW 8 /* MB */
+#define MEMCG_MEMORY_64_THRES_MEDIUM 5 /* MB */
+#define MEMCG_MEMORY_64_THRES_LEAVE 8 /* MB */
+
+/* thresholds for 256M RAM */
+#define PROACTIVE_256_THRES 50 /* MB */
+#define PROACTIVE_256_LEAVE 80 /* MB */
+#define DYNAMIC_256_THRES 10 /* MB */
+#define MEMCG_MEMORY_256_THRES_SWAP 40 /* MB */
+#define MEMCG_MEMORY_256_THRES_LOW 20 /* MB */
+#define MEMCG_MEMORY_256_THRES_MEDIUM 10 /* MB */
+#define MEMCG_MEMORY_256_THRES_LEAVE 20 /* MB */
+
+/* threshold for 448M RAM */
+#define PROACTIVE_448_THRES 80 /* MB */
+#define PROACTIVE_448_LEAVE 100 /* MB */
+#define DYNAMIC_448_THRES 40 /* MB */
+#define MEMCG_MEMORY_448_THRES_SWAP 100 /* MB */
+#define MEMCG_MEMORY_448_THRES_LOW 50 /* MB */
+#define MEMCG_MEMORY_448_THRES_MEDIUM 40 /* MB */
+#define MEMCG_MEMORY_448_THRES_LEAVE 60 /* MB */
+
+/* threshold for 512M RAM */
+#define PROACTIVE_512_THRES 80 /* MB */
+#define PROACTIVE_512_LEAVE 100 /* MB */
+#define DYNAMIC_512_THRES 40 /* 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 PROACTIVE_768_THRES 100 /* MB */
+#define PROACTIVE_768_LEAVE 120 /* MB */
+#define DYNAMIC_768_THRES 50 /* 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 PROACTIVE_1024_THRES 150 /* MB */
+#define PROACTIVE_1024_LEAVE 300 /* MB */
+#define DYNAMIC_1024_THRES 100 /* MB */
+#define MEMCG_MEMORY_1024_THRES_SWAP 300 /* MB */
+#define MEMCG_MEMORY_1024_THRES_LOW 200 /* MB */
+#define MEMCG_MEMORY_1024_THRES_MEDIUM 100 /* MB */
+#define MEMCG_MEMORY_1024_THRES_LEAVE 150 /* MB */
+
+/* threshold for more than 2048M RAM */
+#define PROACTIVE_2048_THRES 200 /* MB */
+#define PROACTIVE_2048_LEAVE 500 /* MB */
+#define DYNAMIC_2048_THRES 160 /* MB */
+#define MEMCG_MEMORY_2048_THRES_SWAP 300 /* MB */
+#define MEMCG_MEMORY_2048_THRES_LOW 200 /* MB */
+#define MEMCG_MEMORY_2048_THRES_MEDIUM 160 /* MB */
+#define MEMCG_MEMORY_2048_THRES_LEAVE 300 /* MB */
+
+static unsigned proactive_threshold;
+static unsigned proactive_leave;
+static unsigned dynamic_threshold_min;
+static unsigned dynamic_threshold_adj_gap;
+static unsigned dynamic_oom_threshold;
+
+struct task_info {
+ pid_t pid;
+ pid_t pgid;
+ int oom_score_adj;
+ int size;
+};
+
+struct lowmem_process_entry {
+ int cur_mem_state;
+ int new_mem_state;
+ void (*action) (void);
+};
+
+/* low memory action function for cgroup */
+static void memory_cgroup_medium_act(int type, struct memcg_info *mi);
+/* low memory action function */
+static void normal_act(void);
+static void swap_act(void);
+static void low_act(void);
+static void medium_act(void);
+
+static Eina_Bool medium_cb(void *data);
+
+#define LOWMEM_ENTRY(c, n, act) \
+ { LOWMEM_##c, LOWMEM_##n, act}
+
+static struct lowmem_process_entry lpe[] = {
+ LOWMEM_ENTRY(NORMAL, SWAP, swap_act),
+ LOWMEM_ENTRY(NORMAL, LOW, low_act),
+ LOWMEM_ENTRY(NORMAL, MEDIUM, medium_act),
+ LOWMEM_ENTRY(SWAP, NORMAL, normal_act),
+ LOWMEM_ENTRY(SWAP, LOW, low_act),
+ LOWMEM_ENTRY(SWAP, MEDIUM, medium_act),
+ LOWMEM_ENTRY(LOW, SWAP, swap_act),
+ LOWMEM_ENTRY(LOW, NORMAL, normal_act),
+ LOWMEM_ENTRY(LOW, MEDIUM, medium_act),
+ LOWMEM_ENTRY(MEDIUM, SWAP, swap_act),
+ LOWMEM_ENTRY(MEDIUM, NORMAL, normal_act),
+ LOWMEM_ENTRY(MEDIUM, LOW, low_act),
+};
+
+static int cur_mem_state = LOWMEM_NORMAL;
+static Ecore_Timer *oom_check_timer;
+static int num_max_victims = MAX_MEMORY_CGROUP_VICTIMS;
+
+static pthread_t oom_thread;
+static pthread_mutex_t oom_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t oom_cond = PTHREAD_COND_INITIALIZER;
+
+static unsigned long totalram;
+static unsigned long ktotalram;
+static int fg_killed;
+
+static const struct module_ops memory_modules_ops;
+static const struct module_ops *lowmem_ops;
+static bool oom_popup_enable;
+static bool oom_popup;
+
+static const char *memcg_name[MEMCG_MAX] = {
+ NULL,
+ "platform",
+ "foreground",
+ "previous",
+ "favorite",
+ "background",
+ "swap",
+};
+
+enum memory_level {
+ MEMORY_LEVEL_NORMAL,
+ MEMORY_LEVEL_LOW,
+ MEMORY_LEVEL_CRITICAL,
+};
+
+/*
+ * This structure has full hierarchy of memory cgroups on running system.
+ * It is exported through lowmem-handler.h file.
+ **/
+static struct memcg **memcg_tree;
+
+/*
+ * Special node that point's to /sys/fs/cgroup/memory - root of memcg group.
+ * This is the same as memcg_tree[MEMCG_MEMORY]->info.
+ */
+static struct memcg_info *memcg_root;
+
+static char *convert_memstate_to_str(int mem_state)
+{
+ char *tmp = NULL;
+ switch (mem_state) {
+ case LOWMEM_NORMAL:
+ tmp = "mem normal";
+ break;
+ case LOWMEM_SWAP:
+ tmp = "mem swap";
+ break;
+ case LOWMEM_LOW:
+ tmp = "mem low";
+ break;
+ case LOWMEM_MEDIUM:
+ tmp = "mem medium";
+ break;
+ default:
+ assert(0);
+ }
+ return tmp;
+}
+
+static void adjust_dynamic_threshold(int victim_memcg)
+{
+ unsigned prev_dynamic_threshold = dynamic_oom_threshold;
+ unsigned available;
+
+ switch (cur_mem_state) {
+ case LOWMEM_NORMAL:
+ available = proc_get_mem_available();
+ if (available > memcg_root->threshold[LOWMEM_MEDIUM])
+ dynamic_oom_threshold = memcg_root->threshold[LOWMEM_MEDIUM];
+ break;
+ case LOWMEM_SWAP:
+ case LOWMEM_LOW:
+ if (victim_memcg <= MEMCG_FAVORITE) {
+ dynamic_oom_threshold -= dynamic_threshold_adj_gap;
+ break;
+ }
+
+ dynamic_oom_threshold += dynamic_threshold_adj_gap;
+
+ if (dynamic_oom_threshold >=
+ memcg_root->threshold[LOWMEM_MEDIUM])
+ dynamic_oom_threshold = memcg_root->threshold[LOWMEM_MEDIUM];
+ break;
+ case LOWMEM_MEDIUM:
+ if (victim_memcg <= MEMCG_FAVORITE)
+ dynamic_oom_threshold -= dynamic_threshold_adj_gap;
+
+ if (dynamic_oom_threshold < dynamic_threshold_min)
+ dynamic_oom_threshold = dynamic_threshold_min;
+ break;
+ default:
+ break;
+ }
+
+ _I("dynamic_threshold is changed from %u to %u, cur_mem_state = %s, victim_memcg = %d",
+ prev_dynamic_threshold, dynamic_oom_threshold,
+ convert_memstate_to_str(cur_mem_state),
+ victim_memcg);
+}
+
+static int lowmem_launch_oompopup(void)
+{
+ return launch_system_app_by_dbus(SYSTEM_POPUP_BUS_NAME,
+ SYSTEM_POPUP_PATH_SYSTEM, SYSTEM_POPUP_IFACE_SYSTEM,
+ "PopupLaunch", 2, "_SYSPOPUP_CONTENT_", "lowmemory_oom");
+}
+
+static inline void get_total_memory(void)
+{
+ struct sysinfo si;
+ if (totalram)
+ return;
+
+ if (!sysinfo(&si)) {
+ totalram = si.totalram;
+ ktotalram = BtoKB(totalram);
+ }
+}
+
+#ifdef HEART_SUPPORT
+static int lowmem_get_proc_mem_uss(pid_t pid, unsigned int *uss)
+{
+ struct proc_app_info *pai = NULL;
+ unsigned int tpss = 0, tuss = 0;
+ int ret;
+
+ pai = find_app_info(pid);
+ if (!pai)
+ goto error;
+
+ ret = heart_memory_get_latest_data(pai->appid, &tpss, &tuss);
+ if (ret == RESOURCED_ERROR_FAIL)
+ goto error;
+ *uss = tuss;
+ _D("success get uss = %u for %s from data crud", tuss, pai->appid);
+ return RESOURCED_ERROR_NONE;
+
+error:
+ *uss = 0;
+ return RESOURCED_ERROR_FAIL;
+}
+#endif
+
+static int get_proc_mem_usage(pid_t pid, unsigned int *usage)
+{
+ int ret;
+#ifdef HEART_SUPPORT
+ static int logging_memory_avaliable = 10;
+
+ if (logging_memory_avaliable > 0) {
+ ret = lowmem_get_proc_mem_uss(pid, usage);
+ if (ret == RESOURCED_ERROR_NONE && *usage > 0)
+ return ret;
+ /*
+ * Calls to logging_memory_get_latest_data are expensive and
+ * often. If we can't get the values, because most probably memory
+ * module is disabled. Let's use the only available alternative.
+ * We try 10 times, before we acknowledge that the module is not
+ * available.
+ */
+ logging_memory_avaliable--;
+ }
+#endif
+
+ /*
+ * We fallback to getting RSS value if we can't get USS.
+ */
+ ret = proc_get_mem_usage(pid, NULL, usage);
+ if (ret == RESOURCED_ERROR_NONE)
+ return ret;
+
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int memps_file_select(const struct dirent *entry)
+{
+ return strstr(entry->d_name, "memps") ? 1 : 0;
+}
+
+int compare_func(const struct dirent **a, const struct dirent **b)
+{
+ const char *fn_a = (*a)->d_name;
+ const char *fn_b = (*b)->d_name;
+ char *str_at = strrchr(fn_a, '_') + 1;
+ char *str_bt = strrchr(fn_b, '_') + 1;
+
+ return strcmp(str_at, str_bt);
+}
+
+static void clear_logs(char *dir)
+{
+ struct dirent **namelist;
+ int n, i, ret;
+ char fname[BUF_MAX];
+
+ n = scandir(dir, &namelist, memps_file_select, compare_func);
+ _D("num of log files %d", n);
+ if (n < MAX_MEMPS_LOGS) {
+ while (n--)
+ free(namelist[n]);
+ free(namelist);
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (i < NUM_RM_LOGS) {
+ snprintf(fname, sizeof(fname), "%s%s", dir, namelist[i]->d_name);
+ _D("remove log file %s", fname);
+ ret = remove(fname);
+ if (ret < 0)
+ _E("%s file cannot removed", fname);
+ }
+
+ free(namelist[i]);
+ }
+ free(namelist);
+}
+
+static void make_memps_log(char *file, pid_t pid, char *victim_name)
+{
+ time_t now;
+ struct tm cur_tm;
+ char new_log[BUF_MAX];
+ static pid_t old_pid;
+
+ if (old_pid == pid)
+ return;
+ old_pid = pid;
+
+ now = time(NULL);
+
+ if (localtime_r(&now, &cur_tm) == NULL) {
+ _E("Fail to get localtime");
+ return;
+ }
+
+ snprintf(new_log, sizeof(new_log),
+ "%s_%s_%d_%.4d%.2d%.2d%.2d%.2d%.2d", file, victim_name,
+ pid, (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);
+
+ if (fork() == 0) {
+ execl(MEMPS_EXEC_PATH, MEMPS_EXEC_PATH, "-r", "-f", new_log, (char *)NULL);
+ exit(0);
+ }
+}
+
+static int lowmem_check_current_state(struct memcg_info *mi)
+{
+ unsigned int usage, oomleave;
+ int ret;
+
+ oomleave = mi->oomleave;
+ ret = memcg_get_anon_usage(mi, &usage);
+
+ if (ret) {
+ _D("getting anonymous usage fails");
+ return ret;
+ }
+
+ if (oomleave > usage) {
+ _D("%s : usage : %u, leave threshold : %u",
+ __func__, usage, oomleave);
+ return RESOURCED_ERROR_NONE;
+ } else {
+ _D("%s : usage : %u, leave threshold: %u",
+ __func__, usage, oomleave);
+ return RESOURCED_ERROR_FAIL;
+ }
+}
+
+static int lowmem_get_task_info_array_for_memcg(struct memcg_info *mi, GArray *tasks_array)
+{
+ int pid_idx, tsk_idx;
+ char appname[BUF_MAX] = {0, };
+
+ GArray *pids_array = g_array_new(false, false, sizeof(pid_t));
+ memcg_get_pids(mi, pids_array);
+
+ if (pids_array->len == 0)
+ /*
+ * if task read in this cgroup fails,
+ * return the current number of victims
+ */
+ return tasks_array->len;
+
+ for (pid_idx = 0; pid_idx < pids_array->len; pid_idx++) {
+ pid_t tpid = 0;
+ int toom = 0;
+ unsigned int tsize = 0;
+
+ tpid = g_array_index(pids_array, pid_t, pid_idx);
+
+ if (proc_get_oom_score_adj(tpid, &toom) < 0 ||
+ toom <= OOMADJ_SERVICE_MIN) {
+ _D("pid(%d) was already terminated or high priority oom = %d",
+ tpid, toom);
+ continue;
+ }
+
+ if (get_proc_mem_usage(tpid, &tsize) < 0) {
+ _D("pid(%d) size is not available\n", tpid);
+ continue;
+ }
+
+ if (proc_get_cmdline(tpid, appname) < 0)
+ continue;
+
+ for (tsk_idx = 0; tsk_idx < tasks_array->len; tsk_idx++) {
+ struct task_info *tsk = &g_array_index(tasks_array,
+ struct task_info, tsk_idx);
+ if (getpgid(tpid) == tsk->pgid) {
+ tsk->size += tsize;
+ if (tsk->oom_score_adj <= 0 && toom > 0) {
+ tsk->pid = tpid;
+ tsk->oom_score_adj = toom;
+ }
+ break;
+ }
+ }
+
+ if (tsk_idx == tasks_array->len) {
+ struct task_info tsk;
+ tsk.pid = tpid;
+ tsk.pgid = getpgid(tpid);
+ tsk.oom_score_adj = toom;
+ tsk.size = tsize;
+
+ g_array_append_val(tasks_array, tsk);
+ }
+
+ }
+
+ g_array_free(pids_array, TRUE);
+ return tasks_array->len;
+}
+
+static void lowmem_kill_victim(struct task_info *tsk,
+ int flags, int memps_log, unsigned int *total_size)
+{
+ pid_t pid;
+ int ret;
+ unsigned int total = 0;
+ char appname[PATH_MAX];
+ int sigterm;
+
+ pid = tsk->pid;
+
+ if (pid <= 0 || pid == getpid())
+ return;
+
+ ret = proc_get_cmdline(pid, appname);
+ if (ret == RESOURCED_ERROR_FAIL)
+ return;
+
+ if (!strcmp("memps", appname) ||
+ !strcmp("crash-worker", appname) ||
+ !strcmp("system-syspopup", appname)) {
+ _E("%s(%d) was selected, skip it", appname, pid);
+ return;
+ }
+
+ /* make memps log for killing application firstly */
+ if (!memps_log)
+ make_memps_log(MEMPS_LOG_FILE, pid, appname);
+
+ total += *total_size + ((float)tsk->size * LOWMEM_RSS_RATIO);
+
+ resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
+ pid, NULL, NULL, PROC_TYPE_NONE);
+
+ if (tsk->oom_score_adj < OOMADJ_BACKGRD_LOCKED) {
+ sigterm = 1;
+ } else if (tsk->oom_score_adj == OOMADJ_BACKGRD_LOCKED) {
+ int app_flag = proc_get_appflag(pid);
+ sigterm = app_flag & PROC_SIGTERM;
+ } else
+ sigterm = 0;
+
+ if (sigterm)
+ kill(pid, SIGTERM);
+ else
+ kill(pid, SIGKILL);
+
+ _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, tsk->oom_score_adj,
+ tsk->size, total, sigterm);
+ *total_size = total;
+
+ if (tsk->oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
+ return;
+
+ if (oom_popup_enable && !oom_popup) {
+ lowmem_launch_oompopup();
+ oom_popup = true;
+ }
+ if (memps_log)
+ make_memps_log(MEMPS_LOG_FILE, pid, appname);
+}
+
+/* return RESOURCED_ERROR_NONE when kill should be continued */
+static int lowmem_check_kill_continued(struct task_info *tsk, int flags)
+{
+ unsigned int available;
+
+ /*
+ * Processes with the priority higher than perceptible are killed
+ * only when the available memory is less than dynamic oom threshold.
+ */
+ if (tsk->oom_score_adj > OOMADJ_BACKGRD_PERCEPTIBLE)
+ return RESOURCED_ERROR_NONE;
+
+ if ((flags & OOM_FORCE) || !(flags & OOM_TIMER_CHECK)) {
+ _I("%d is skipped during force kill, flag = %d",
+ tsk->pid, flags);
+ return RESOURCED_ERROR_FAIL;
+ }
+ available = proc_get_mem_available();
+ if (available > dynamic_oom_threshold) {
+ _I("available: %d MB, larger than %u MB, do not kill foreground",
+ available, dynamic_oom_threshold);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int compare_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_victims_point(const struct task_info *ta, const struct task_info *tb)
+{
+ unsigned int pa, pb;
+ assert(ta != NULL);
+ assert(tb != NULL);
+
+ /*
+ * followed by kernel badness point calculation using heuristic.
+ * oom_score_adj is normalized by its unit, which varies -1000 ~ 1000.
+ * Since we only consider tasks with oom_score_adj larger than 0
+ * as victim candidates, point always has positive value.
+ */
+ pa = ta->oom_score_adj * (ktotalram / 1000) + ta->size;
+ pb = tb->oom_score_adj * (ktotalram / 1000) + tb->size;
+
+ return pb - pa;
+}
+
+static int lowmem_kill_cgroup_victims(int type, struct memcg_info *mi,
+ int max_victims, unsigned should_be_freed, int flags,
+ unsigned int *total_size, int *completed)
+{
+ int i, ret, victim = 0, count = 0;
+ unsigned total_victim_size = 0;
+ GArray *candidates = NULL;
+
+ candidates = g_array_new(false, false, sizeof(struct task_info));
+
+ /* if g_array_new fails, return the current number of victims */
+ if (candidates == NULL)
+ return victim;
+
+ /*
+ * if there is no tasks in this cgroup,
+ * return the current number of victims
+ */
+ count = lowmem_get_task_info_array_for_memcg(mi, candidates);
+ if (count == 0) {
+ g_array_free(candidates, true);
+ return victim;
+ }
+
+ g_array_sort(candidates,
+ (GCompareFunc)compare_victims);
+
+ for (i = 0; i < candidates->len; i++) {
+ struct task_info *tsk;
+ if (i >= max_victims ||
+ (!(flags & OOM_NOMEMORY_CHECK) &&
+ total_victim_size >= MBtoKB(should_be_freed))) {
+ _E("victim = %d, max_victims = %d, total_size = %u",
+ i, max_victims, total_victim_size);
+ break;
+ }
+
+ tsk = &g_array_index(candidates, struct task_info, i);
+
+ ret = lowmem_check_kill_continued(tsk, flags);
+ if (ret == RESOURCED_ERROR_FAIL && completed) {
+ _E("checked kill continued and completed");
+ *completed = 1;
+ break;
+ }
+
+ lowmem_kill_victim(tsk, flags, i, &total_victim_size);
+ }
+
+ victim = i;
+ g_array_free(candidates, true);
+ *total_size = total_victim_size;
+
+ return victim;
+}
+
+static inline int is_dynamic_process_killer(int flags)
+{
+ return (flags & OOM_FORCE) && !(flags & OOM_NOMEMORY_CHECK);
+}
+
+static int lowmem_kill_subcgroup_victims(int type, int max_victims, int flags,
+ unsigned int *total_size, int *completed)
+{
+ GSList *iter = NULL;
+ GArray *candidates = NULL;
+ int i, ret, victim = 0;
+ unsigned int total_victim_size = 0;
+ struct task_info *tsk;
+
+ candidates = g_array_new(false, false, sizeof(struct task_info));
+ gslist_for_each_item(iter, memcg_tree[type]->cgroups) {
+ struct memcg_info *mi =
+ (struct memcg_info *)(iter->data);
+ int count = lowmem_get_task_info_array_for_memcg(mi, candidates);
+ _D("get %d pids", count);
+ }
+
+ g_array_sort(candidates, (GCompareFunc)compare_victims);
+
+ for (i = 0; i < candidates->len; i++) {
+ if (i == max_victims)
+ break;
+
+ tsk = &g_array_index(candidates, struct task_info, i);
+
+ ret = lowmem_check_kill_continued(tsk, flags);
+ if (ret == RESOURCED_ERROR_FAIL)
+ break;
+
+ lowmem_kill_victim(tsk, flags, i, &total_victim_size);
+ }
+
+ victim = i;
+ g_array_free(candidates, true);
+ return victim;
+}
+
+static unsigned int is_memory_recovered(unsigned int *avail, unsigned int *thres)
+{
+ unsigned int available = proc_get_mem_available();
+ unsigned int leave_threshold = memcg_root->threshold_leave;
+ unsigned int should_be_freed = 0;
+
+ if (available < leave_threshold)
+ should_be_freed = leave_threshold - available;
+ /*
+ * free THRESHOLD_MARGIN more than real should be freed,
+ * because launching app is consuming up the memory.
+ */
+ if (should_be_freed > 0)
+ should_be_freed += THRESHOLD_MARGIN;
+
+ *avail = available;
+ *thres = leave_threshold;
+
+ _I("should_be_freed = %u MB", should_be_freed);
+ return should_be_freed;
+}
+
+static int lowmem_get_pids_proc(GArray *pids)
+{
+ DIR *dp;
+ struct dirent dentry;
+ struct dirent *result;
+ int ret;
+ char appname[PROC_NAME_MAX] = {0};
+
+ dp = opendir("/proc");
+ if (!dp) {
+ _E("fail to open /proc");
+ return RESOURCED_ERROR_FAIL;
+ }
+ while (!(ret = readdir_r(dp, &dentry, &result)) && result != NULL) {
+ struct task_info tsk;
+ pid_t pid = 0, pgid = 0;
+ int oom = 0;
+ unsigned int size = 0;
+
+ if (!isdigit(dentry.d_name[0]))
+ continue;
+
+ pid = (pid_t)atoi(dentry.d_name);
+ pgid = getpgid(pid);
+ if (!pgid)
+ continue;
+
+ if (proc_get_oom_score_adj(pid, &oom) < 0) {
+ _D("pid(%d) was already terminated", pid);
+ continue;
+ }
+
+ if (get_proc_mem_usage(pid, &size) < 0) {
+ _D("pid(%d) size is not available\n", pid);
+ continue;
+ }
+
+ if (proc_get_cmdline(pid, appname) < 0)
+ continue;
+
+ tsk.pid = pid;
+ tsk.pgid = pgid;
+ tsk.oom_score_adj = oom;
+ tsk.size = size;
+
+ g_array_append_val(pids, tsk);
+ }
+
+ closedir(dp);
+ if (ret)
+ _E("fail to open subdirectory in /proc");
+
+ if (pids->len)
+ return pids->len;
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int lowmem_kill_memory_cgroup_victims(int flags)
+{
+ GArray *candidates = NULL;
+ int i, count, victim = 0;
+ unsigned int av, total_victim_size = 0;
+ struct task_info *tsk;
+
+ candidates = g_array_new(false, false, sizeof(struct task_info));
+
+ /* if g_array_new fails, return the current number of victims */
+ if (candidates == NULL)
+ return victim;
+
+ count = lowmem_get_pids_proc(candidates);
+
+ if (count <= 0)
+ return victim;
+
+ g_array_sort(candidates, (GCompareFunc)compare_victims_point);
+
+ _I("start to kill for memory cgroup");
+ for (i = 0; i < candidates->len; i++) {
+ tsk = &g_array_index(candidates, struct task_info, i);
+
+ av = proc_get_mem_available();
+
+ if (av > dynamic_oom_threshold || i >= num_max_victims) {
+ _I("checking proc, available: %d MB, larger than threshold margin", av);
+ g_array_free(candidates, true);
+ victim = i;
+ return victim;
+ }
+ lowmem_kill_victim(tsk, flags, i, &total_victim_size);
+ }
+ victim = i;
+ g_array_free(candidates, true);
+ return victim;
+}
+
+/* Find victims: (SWAP -> ) BACKGROUND */
+static int lowmem_kill_all_cgroup_victims(int flags, int *completed)
+{
+ int i, count = 0;
+ unsigned int available = 0, should_be_freed = 0, leave_threshold = 0;
+ struct memcg_info *mi;
+ unsigned int total_size = 0;
+
+ for (i = MEMCG_MAX - 1; i > 0; i--) {
+ adjust_dynamic_threshold(i);
+
+ should_be_freed = is_memory_recovered(&available, &leave_threshold);
+
+ if (should_be_freed == 0)
+ return count;
+
+ if (!memcg_tree[i] || !memcg_tree[i]->info)
+ continue;
+
+ mi = memcg_tree[i]->info;
+
+ /*
+ * Processes in the previous cgroup are killed only when
+ * the available memory is less than dynamic oom threshold.
+ */
+ if ((i <= MEMCG_PREVIOUS) &&
+ (available > dynamic_oom_threshold)) {
+ _E("do not try fg group, %u > %u, completed",
+ available, dynamic_oom_threshold);
+
+ if (completed)
+ *completed = 1;
+
+ return count;
+ }
+
+ _I("%s start, available = %u, should_be_freed = %u",
+ mi->name, available, should_be_freed);
+
+ if (memcg_tree[i]->use_hierarchy)
+ count = lowmem_kill_subcgroup_victims(i, num_max_victims,
+ flags, &total_size, completed);
+ else
+ count = lowmem_kill_cgroup_victims(i, mi,
+ num_max_victims, should_be_freed,
+ flags, &total_size, completed);
+
+ if (count == 0) {
+ _E("%s: there is no victim", mi->name);
+ continue;
+ }
+
+ if (completed && *completed) {
+ _E("completed after kill %s cgroup", mi->name);
+ break;
+ }
+
+ if ((flags & OOM_TIMER_CHECK) && (i <= MEMCG_PREVIOUS)) {
+ if (++fg_killed >= MAX_FGRD_KILL) {
+ _E("foreground is killed %d times and search from proc", fg_killed);
+ fg_killed = 0;
+ continue;
+ }
+ _E("foreground is killed %d times", fg_killed);
+ }
+
+ _E("%s: kill %d victims, total_size = %u",
+ mi->name, count, total_size);
+ return count;
+ }
+
+ if (completed && !(*completed))
+ count = lowmem_kill_memory_cgroup_victims(flags);
+
+ return count;
+}
+
+static int lowmem_kill_victims(int type, struct memcg_info *mi, int flags,
+ int *completed)
+{
+ unsigned int total_size = 0;
+ int count;
+
+ if (type == MEMCG_MEMORY)
+ count = lowmem_kill_all_cgroup_victims(flags, completed);
+ else
+ count = lowmem_kill_cgroup_victims(type, mi,
+ MAX_CGROUP_VICTIMS, mi->threshold_leave,
+ flags, &total_size, completed);
+
+ return count;
+}
+
+static int lowmem_oom_killer_cb(int type, struct memcg_info *mi, int flags,
+ int *completed)
+{
+ int count = 0;
+
+ /* get multiple victims from /sys/fs/cgroup/memory/.../tasks */
+ count = lowmem_kill_victims(type, mi, flags, completed);
+
+ if (count == 0) {
+ _D("victim count = %d", count);
+ return count;
+ }
+
+ /* check current memory status */
+ if (!(flags & OOM_FORCE) && type != MEMCG_MEMORY &&
+ lowmem_check_current_state(mi) >= 0)
+ return count;
+
+ clear_logs(MEMPS_LOG_PATH);
+ return count;
+}
+
+static int lowmem_force_oom_killer(int flags, unsigned int should_be_freed,
+ int max_victims)
+{
+ int count = 0, completed = 0, i;
+ unsigned int total_size, freed = 0;
+
+ lowmem_change_memory_state(LOWMEM_LOW, 1);
+ for (i = MEMCG_MAX - 1; i >= MEMCG_BACKGROUND; i--) {
+ int num_max = max_victims - count;
+ unsigned int remained = should_be_freed - freed;
+ count += lowmem_kill_cgroup_victims(i, memcg_tree[i]->info,
+ num_max, remained, flags, &total_size, &completed);
+ freed += KBtoMB(total_size);
+ _D("force kill total %d victims, freed = %u", count, freed);
+ if (should_be_freed > 0 && freed >= should_be_freed)
+ break;
+ }
+ lowmem_change_memory_state(LOWMEM_NORMAL, 0);
+
+ return count;
+}
+
+static void *lowmem_oom_killer_pthread(void *arg)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ setpriority(PRIO_PROCESS, 0, OOM_KILLER_PRIORITY);
+
+ while (1) {
+ /*
+ * When signalled by main thread,
+ * it starts lowmem_oom_killer_cb().
+ */
+ ret = pthread_mutex_lock(&oom_mutex);
+ if (ret) {
+ _E("oom thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&oom_cond, &oom_mutex);
+ if (ret) {
+ _E("oom thread::pthread_cond_wait() failed, %d", ret);
+ pthread_mutex_unlock(&oom_mutex);
+ break;
+ }
+
+ _I("oom thread conditional signal received and start");
+ lowmem_oom_killer_cb(MEMCG_MEMORY, memcg_root, OOM_NONE, NULL);
+
+ _I("lowmem_oom_killer_cb finished");
+
+ ret = pthread_mutex_unlock(&oom_mutex);
+ if (ret) {
+ _E("oom thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* Now our thread finishes - cleanup TID */
+ oom_thread = 0;
+
+ return NULL;
+}
+
+static void change_lowmem_state(unsigned int mem_state)
+{
+ if (cur_mem_state == mem_state)
+ return;
+
+ _I("[LOW MEM STATE] %s ==> %s", convert_memstate_to_str(cur_mem_state),
+ convert_memstate_to_str(mem_state));
+ cur_mem_state = mem_state;
+
+ adjust_dynamic_threshold(MEMCG_BACKGROUND);
+}
+
+static void lowmem_swap_memory(enum memcg_type type, struct memcg_info *mi)
+{
+ unsigned int available;
+ struct swap_status_msg msg;
+ static const struct module_ops *swap;
+
+ if (cur_mem_state == LOWMEM_NORMAL)
+ return;
+
+ if (!swap) {
+ swap = find_module("swap");
+ if(!swap)
+ return;
+ }
+
+ available = proc_get_mem_available();
+ if (cur_mem_state != LOWMEM_SWAP &&
+ available <= memcg_root->threshold[LOWMEM_SWAP])
+ swap_act();
+
+ msg.type = type;
+ msg.info = mi;
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_START, &msg);
+}
+
+void lowmem_trigger_swap(pid_t pid, int memcg_idx)
+{
+ struct memcg_info *mi;
+ struct swap_status_msg msg;
+
+ mi = memcg_tree[memcg_idx]->info;
+ _D("name : %s, pid : %d", mi->name, pid);
+ cgroup_write_node(mi->name, CGROUP_FILE_NAME, pid);
+ msg.type = memcg_idx;
+ msg.info = mi;
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_START, &msg);
+}
+
+static void memory_level_send_system_event(int lv)
+{
+ bundle *b;
+ const char *str;
+
+ 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;
+ }
+
+ b = bundle_create();
+ if (!b) {
+ _E("Failed to create bundle");
+ return;
+ }
+
+ bundle_add_str(b, EVT_KEY_LOW_MEMORY, str);
+ eventsystem_send_system_event(SYS_EVENT_LOW_MEMORY, b);
+ bundle_free(b);
+}
+
+static void normal_act(void)
+{
+ int ret, status;
+
+ 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) {
+ vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+ VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+ memory_level_send_system_event(MEMORY_LEVEL_NORMAL);
+ }
+
+ change_lowmem_state(LOWMEM_NORMAL);
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_UNSET_LIMIT, NULL);
+ if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_ENABLED);
+}
+
+static void swap_act(void)
+{
+ int ret, status;
+
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+ if (ret)
+ _E("vconf get failed %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+
+ 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(LOWMEM_SWAP);
+ if (proc_get_freezer_status() == CGROUP_FREEZER_PAUSED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_ENABLED);
+
+ if (swap_get_state() != SWAP_ON)
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_ACTIVATE, NULL);
+}
+
+static void low_act(void)
+{
+ int ret, status;
+
+ ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+
+ if (ret)
+ _D("vconf_get_int fail %s", VCONFKEY_SYSMAN_LOW_MEMORY);
+
+ if (proc_get_freezer_status() == CGROUP_FREEZER_ENABLED)
+ resourced_notify(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ (void *)CGROUP_FREEZER_PAUSED);
+ change_lowmem_state(LOWMEM_LOW);
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_COMPACT, (void *)SWAP_COMPACT_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) {
+ 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;
+ int count = 0;
+ int completed = 0;
+
+ available = proc_get_mem_available();
+ _I("available = %u, timer run until reaching leave threshold", available);
+
+ if (available >= memcg_root->threshold_leave && oom_check_timer != NULL) {
+ ecore_timer_del(oom_check_timer);
+ oom_check_timer = NULL;
+ _I("oom_check_timer deleted after reaching leave threshold");
+ normal_act();
+ fg_killed = 0;
+ oom_popup = false;
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ _I("available = %u cannot reach leave threshold %u, timer again",
+ available, memcg_root->threshold_leave);
+ count = lowmem_oom_killer_cb(MEMCG_MEMORY,
+ memcg_root, OOM_TIMER_CHECK, &completed);
+
+ /*
+ * After running oom killer in timer, but there is no victim,
+ * stop timer.
+ */
+ if (oom_check_timer != NULL &&
+ (completed || (!count && available >= dynamic_oom_threshold))) {
+ ecore_timer_del(oom_check_timer);
+ oom_check_timer = NULL;
+ _I("timer deleted, avail:%u, thres:%u, count:%d, completed:%d",
+ available, dynamic_oom_threshold, count, completed);
+ normal_act();
+ fg_killed = 0;
+ oom_popup = false;
+ return ECORE_CALLBACK_CANCEL;
+ }
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void medium_act(void)
+{
+ int ret = 0;
+
+ change_lowmem_state(LOWMEM_MEDIUM);
+
+ /* signal to lowmem_oom_killer_pthread to start killer */
+ ret = pthread_mutex_trylock(&oom_mutex);
+ if (ret) {
+ _E("medium_act::pthread_mutex_trylock() failed, %d, errno: %d", ret, errno);
+ return;
+ }
+ _I("oom mutex trylock success");
+ pthread_cond_signal(&oom_cond);
+ _I("send signal to oom killer thread");
+ pthread_mutex_unlock(&oom_mutex);
+
+ 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");
+ oom_check_timer =
+ ecore_timer_add(OOM_TIMER_INTERVAL, medium_cb, (void *)NULL);
+ }
+ resourced_notify(RESOURCED_NOTIFIER_SWAP_COMPACT, (void *)SWAP_COMPACT_LOWMEM_MEDIUM);
+
+ return;
+}
+
+static void lowmem_dump_cgroup_procs(struct memcg_info *mi)
+{
+ int i;
+ unsigned int size;
+ pid_t pid;
+ GArray *pids_array = g_array_new(false, false, sizeof(pid_t));
+
+ memcg_get_pids(mi, pids_array);
+
+ for (i = 0; i < pids_array->len; i++) {
+ pid = g_array_index(pids_array, pid_t, i);
+ get_proc_mem_usage(pid, &size);
+ _I("pid = %d, size = %u KB", pid, size);
+ }
+ g_array_free(pids_array, TRUE);
+}
+
+static void memory_cgroup_medium_act(int type, struct memcg_info *mi)
+{
+ _I("[LOW MEM STATE] memory cgroup %s oom state",
+ mi->name);
+
+ /* To Do: only start to kill fg victim when no pending fg victim */
+ lowmem_dump_cgroup_procs(mi);
+ lowmem_oom_killer_cb(type, mi, OOM_NONE, NULL);
+}
+
+static unsigned int check_mem_state(unsigned int available)
+{
+ int mem_state;
+ for (mem_state = LOWMEM_MAX_LEVEL - 1; mem_state > LOWMEM_NORMAL; mem_state--) {
+ if (mem_state != LOWMEM_MEDIUM &&
+ available <= memcg_root->threshold[mem_state])
+ break;
+ else if (mem_state == LOWMEM_MEDIUM &&
+ available <= dynamic_oom_threshold)
+ break;
+ }
+
+ return mem_state;
+}
+
+static int load_vip_config(struct parse_result *result, void *user_data)
+{
+ pid_t pid = 0;
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, MEM_VIP_SECTION))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strcmp(result->name, MEM_VIP_PREDEFINE)) {
+ pid = find_pid_from_cmdline(result->value);
+ if (pid > 0)
+ proc_set_oom_score_adj(pid, OOMADJ_SERVICE_MIN);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int load_mem_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, MEM_POPUP_SECTION))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strcmp(result->name, MEM_POPUP_STRING)) {
+ if (!strcmp(result->value, "yes"))
+ oom_popup_enable = 1;
+ else if (!strcmp(result->value, "no"))
+ oom_popup_enable = 0;
+ }
+
+ _I("oom_popup_enable = %d", oom_popup_enable);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int set_memory_config(const char *section_name, const struct parse_result *result)
+{
+ if (!result || !section_name)
+ return -EINVAL;
+
+ if (strcmp(result->section, section_name))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strcmp(result->name, "ThresholdSwap")) {
+ int value = atoi(result->value);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, value);
+ } else if (!strcmp(result->name, "ThresholdLow")) {
+ int value = atoi(result->value);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, value);
+ } else if (!strcmp(result->name, "ThresholdMedium")) {
+ int value = atoi(result->value);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, value);
+ } else if (!strcmp(result->name, "ThresholdLeave")) {
+ int value = atoi(result->value);
+ lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, value);
+ } else if (!strcmp(result->name, "ForegroundRatio")) {
+ float ratio = atof(result->value);
+ memcg_info_set_limit(memcg_tree[MEMCG_FOREGROUND]->info, ratio, totalram);
+ } else if (!strcmp(result->name, "ForegroundUseHierarchy")) {
+ int use_hierarchy = atoi(result->value);
+ memcg_tree[MEMCG_FOREGROUND]->use_hierarchy = use_hierarchy;
+ } else if (!strcmp(result->name, "ForegroundNumCgroups")) {
+ int num_cgroups = atoi(result->value);
+ memcg_add_cgroups(memcg_tree[MEMCG_FOREGROUND],
+ num_cgroups);
+ memcg_show(memcg_tree[MEMCG_FOREGROUND]);
+ } else if (!strcmp(result->name, "NumMaxVictims")) {
+ int value = atoi(result->value);
+ num_max_victims = value;
+ } else if (!strcmp(result->name, "ProactiveThreshold")) {
+ int value = atoi(result->value);
+ proactive_threshold = value;
+ } else if (!strcmp(result->name, "ProactiveLeave")) {
+ int value = atoi(result->value);
+ proactive_leave = value;
+ } else if (!strcmp(result->name, "DynamicThreshold")) {
+ int value = atoi(result->value);
+ dynamic_threshold_min = value;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int memory_load_64_config(struct parse_result *result, void *user_data)
+{
+ return set_memory_config("Memory64", result);
+}
+
+static int memory_load_256_config(struct parse_result *result, void *user_data)
+{
+ return set_memory_config("Memory256", result);
+}
+
+static int memory_load_448_config(struct parse_result *result, void *user_data)
+{
+ return set_memory_config("Memory448", result);
+}
+
+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);
+}
+
+static int memory_load_2048_config(struct parse_result *result, void *user_data)
+{
+ return set_memory_config("Memory2048", result);
+}
+
+/* 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 */
+ proactive_threshold = PROACTIVE_64_THRES;
+ proactive_leave = PROACTIVE_64_LEAVE;
+ dynamic_threshold_min = DYNAMIC_64_THRES;
+ 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 */
+ proactive_threshold = PROACTIVE_256_THRES;
+ proactive_leave = PROACTIVE_256_LEAVE;
+ dynamic_threshold_min = DYNAMIC_256_THRES;
+ 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_448) {
+ /* set thresholds for ram size 448M */
+ proactive_threshold = PROACTIVE_448_THRES;
+ proactive_leave = PROACTIVE_448_LEAVE;
+ dynamic_threshold_min = DYNAMIC_448_THRES;
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_SWAP, MEMCG_MEMORY_448_THRES_SWAP);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_LOW, MEMCG_MEMORY_448_THRES_LOW);
+ lowmem_memcg_set_threshold(MEMCG_MEMORY, LOWMEM_MEDIUM, MEMCG_MEMORY_448_THRES_MEDIUM);
+ lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, MEMCG_MEMORY_448_THRES_LEAVE);
+ config_parse(MEM_CONF_FILE, memory_load_448_config, NULL);
+ } else if (total_ramsize <= MEM_SIZE_512) {
+ /* set thresholds for ram size 512M */
+ proactive_threshold = PROACTIVE_512_THRES;
+ proactive_leave = PROACTIVE_512_LEAVE;
+ dynamic_threshold_min = DYNAMIC_512_THRES;
+ 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 */
+ proactive_threshold = PROACTIVE_768_THRES;
+ proactive_leave = PROACTIVE_768_LEAVE;
+ dynamic_threshold_min = DYNAMIC_768_THRES;
+ 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 */
+ proactive_threshold = PROACTIVE_1024_THRES;
+ proactive_leave = PROACTIVE_1024_LEAVE;
+ dynamic_threshold_min = DYNAMIC_1024_THRES;
+ 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 {
+ proactive_threshold = PROACTIVE_2048_THRES;
+ proactive_leave = PROACTIVE_2048_LEAVE;
+ dynamic_threshold_min = DYNAMIC_2048_THRES;
+ 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);
+ }
+
+ if (memcg_root->threshold[LOWMEM_MEDIUM] - dynamic_threshold_min > 0)
+ dynamic_threshold_adj_gap =
+ (memcg_root->threshold[LOWMEM_MEDIUM] -
+ dynamic_threshold_min) >> 2;
+ dynamic_oom_threshold = memcg_root->threshold[LOWMEM_MEDIUM];
+
+ for (i = LOWMEM_SWAP; i < LOWMEM_MAX_LEVEL; i++)
+ _I("set threshold for state '%s' to %u MB", convert_memstate_to_str(i), memcg_root->threshold[i]);
+
+ _I("set number of max victims as %d", num_max_victims);
+ _I("set threshold leave to %u MB", memcg_root->threshold_leave);
+ _I("set proactive threshold to %u MB", proactive_threshold);
+ _I("set proactive low memory killer leave to %u MB", proactive_leave);
+ _I("set dynamic_oom_threshold = %u MB, dynamic_threshold_gap = %u MB,"
+ " dynamic threshold min to %u MB",
+ dynamic_oom_threshold, dynamic_threshold_adj_gap,
+ dynamic_threshold_min);
+}
+
+static int init_memcg_params(void)
+{
+ int idx = 0;
+ char buf[MAX_PATH_LENGTH];
+ memcg_tree = (struct memcg **)malloc(sizeof(struct memcg *) *
+ MEMCG_MAX);
+ if (!memcg_tree)
+ return RESOURCED_ERROR_FAIL;
+
+ for (idx = 0; idx < MEMCG_MAX; idx++) {
+ struct memcg_info *mi = NULL;
+ memcg_tree[idx] = (struct memcg *)malloc(sizeof(struct memcg));
+ if (!memcg_tree[idx]) {
+ int i;
+ for (i = 0; i < idx - 1; i++)
+ free(memcg_tree[i]);
+ free(memcg_tree);
+ return RESOURCED_ERROR_FAIL;
+ }
+ memcg_init(memcg_tree[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 *)malloc(sizeof(struct memcg_info));
+ if (!mi) {
+ int i;
+ for (i = 0; i < idx; i++)
+ free(memcg_tree[i]);
+ free(memcg_tree);
+ return RESOURCED_ERROR_FAIL;
+ }
+ memcg_info_init(mi, buf);
+ memcg_tree[idx]->info = mi;
+ _I("init memory cgroup for %s", buf);
+ if (idx == MEMCG_MEMORY)
+ memcg_root = memcg_tree[idx]->info;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int write_params_memcg_info(struct memcg_info *mi,
+ int write_limit)
+{
+ 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 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;
+
+ /* disable memcg OOM-killer */
+ ret = cgroup_write_node(name,
+ MEMCG_OOM_CONTROL_PATH, 1);
+ if (ret)
+ return ret;
+
+ /* write limit_in_bytes */
+ ret = cgroup_write_node(name,
+ MEMCG_LIMIT_PATH, limit);
+ _I("set %s's limit to %u", name, limit);
+ return ret;
+}
+
+static int write_memcg_params(void)
+{
+ unsigned int i;
+ GSList *iter = NULL;
+
+ for (i = 0; i < MEMCG_MAX; i++) {
+ struct memcg_info *mi = memcg_tree[i]->info;
+ int write_limit = !memcg_tree[i]->use_hierarchy;
+ GSList *list = memcg_tree[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 *mi =
+ (struct memcg_info *)(iter->data);
+ write_params_memcg_info(mi, write_limit);
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct memcg_info *find_foreground_cgroup(struct proc_app_info *pai)
+{
+ unsigned int usage;
+ unsigned int min_usage = UINT_MAX;
+ struct memcg_info *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 (pai && (pai->memory.memcg_idx == MEMCG_FOREGROUND)) {
+ _D("%s is already in foreground", pai->appid);
+ return pai->memory.memcg_info;
+ }
+
+ /*
+ * if any of the process in this group is not in foreground,
+ * find foreground cgroup with minimum usage
+ */
+ if (memcg_tree[MEMCG_FOREGROUND]->use_hierarchy) {
+ gslist_for_each_item(iter,
+ memcg_tree[MEMCG_FOREGROUND]->cgroups) {
+ mi = (struct memcg_info *)(iter->data);
+
+ memcg_get_usage(mi, &usage);
+ /* select foreground memcg with no task first */
+ if (usage == 0) {
+ _D("%s' usage is 0, selected", mi->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);
+
+ } else {
+ return memcg_tree[MEMCG_FOREGROUND]->info;
+ }
+
+ return min_mi;
+}
+
+static void lowmem_move_memcgroup(int pid, int oom_score_adj)
+{
+ struct proc_app_info *pai = find_app_info(pid);
+ struct memcg_info *mi;
+ int memcg_idx, should_swap = 0;
+
+ if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED + OOMADJ_APP_INCREASE) {
+ if (pai && (oom_score_adj != pai->memory.oom_score_adj))
+ proc_set_process_memory_state(pai, pai->memory.memcg_idx,
+ pai->memory.memcg_info, oom_score_adj);
+ return;
+ } else if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+ memcg_idx = MEMCG_BACKGROUND;
+ mi = memcg_tree[memcg_idx]->info;
+ should_swap = 1;
+ } else if (oom_score_adj >= OOMADJ_PREVIOUS_BACKGRD) {
+ memcg_idx = MEMCG_PREVIOUS;
+ mi = memcg_tree[memcg_idx]->info;
+ } else if (oom_score_adj == OOMADJ_FAVORITE) {
+ memcg_idx = MEMCG_FAVORITE;
+ mi = memcg_tree[memcg_idx]->info;
+ should_swap = 1;
+ } else if (oom_score_adj == OOMADJ_SERVICE_DEFAULT) {
+ memcg_idx = MEMCG_PREVIOUS;
+ mi = memcg_tree[memcg_idx]->info;
+ } else if (oom_score_adj >= OOMADJ_BACKGRD_PERCEPTIBLE) {
+ memcg_idx = MEMCG_PREVIOUS;
+ mi = memcg_tree[memcg_idx]->info;
+ } else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED ||
+ oom_score_adj == OOMADJ_INIT) {
+ /*
+ * When resume occurs, to prevent resuming process
+ * from being killed, raise its oom score to OOMADJ_INIT.
+ * However, it could be still in the background group, and
+ * selected as a victim. So, we move it to foreground group
+ * in advanve.
+ */
+ memcg_idx = MEMCG_FOREGROUND;
+ mi = find_foreground_cgroup(pai);
+ } else {
+ return;
+ }
+
+ _D("pid: %d, proc_name: %s, cg_name: %s, oom_score_adj: %d", pid,
+ pai ? pai->appid : "---", memcg_name[memcg_idx],
+ oom_score_adj);
+ cgroup_write_node(mi->name, CGROUP_FILE_NAME, pid);
+ proc_set_process_memory_state(pai, memcg_idx, mi, oom_score_adj);
+
+ /*
+ * We should first move process to cgroup and then start reclaim on that
+ * cgroup.
+ */
+ if (should_swap)
+ lowmem_swap_memory(memcg_idx, memcg_tree[memcg_idx]->info);
+
+}
+
+static int oom_thread_create(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (oom_thread) {
+ _I("oom thread %u already created", (unsigned)oom_thread);
+ } else {
+ /* initialize oom killer thread */
+ ret = pthread_create(&oom_thread, NULL, (void *)lowmem_oom_killer_pthread, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for lowmem_oom_killer_pthread failed, %d\n", ret);
+ oom_thread = 0;
+ } else {
+ pthread_detach(oom_thread);
+ }
+ }
+
+ return ret;
+}
+
+static int create_memcgs(void)
+{
+ int i = 0;
+ int ret = RESOURCED_ERROR_NONE;
+ GSList *iter = NULL;
+ struct memcg_info *mi;
+ char *name;
+
+ /* skip for memory cgroup */
+ for (i = 0; i < MEMCG_MAX; i++) {
+ if (!memcg_name[i])
+ continue;
+ mi = memcg_tree[i]->info;
+ name = mi->name;
+ ret = make_cgroup_subdir(NULL, name, NULL);
+ if (!memcg_tree[i]->use_hierarchy)
+ continue;
+ _D("create memory cgroup for %s, ret = %d", name, ret);
+ /* create sub cgroups */
+ gslist_for_each_item(iter, memcg_tree[i]->cgroups) {
+ mi = (struct memcg_info *)
+ iter->data;
+ name = mi->name;
+ ret = make_cgroup_subdir(NULL, name, NULL);
+ _D("make cgroup subdir for %s, ret = %d", name, ret);
+ }
+ }
+
+ return ret;
+}
+
+static unsigned int lowmem_press_eventfd_read(int fd)
+{
+ unsigned int ret;
+ uint64_t dummy_state;
+
+ ret = read(fd, &dummy_state, sizeof(dummy_state));
+ return ret;
+}
+
+static void lowmem_press_root_cgroup_handler(void)
+{
+ static unsigned int prev_available;
+ unsigned int available;
+ int i, mem_state;
+
+ available = proc_get_mem_available();
+ if (prev_available == available)
+ return;
+
+ mem_state = check_mem_state(available);
+ for (i = 0; i < ARRAY_SIZE(lpe); i++) {
+ if ((cur_mem_state == lpe[i].cur_mem_state)
+ && (mem_state == lpe[i].new_mem_state)) {
+ _D("cur_mem_state = %s, new_mem_state = %s",
+ convert_memstate_to_str(cur_mem_state),
+ convert_memstate_to_str(mem_state));
+ lpe[i].action();
+ }
+ }
+ prev_available = available;
+}
+
+static void lowmem_press_cgroup_handler(int type, struct memcg_info *mi)
+{
+ unsigned int usage, threshold;
+ int ret;
+
+ ret = memcg_get_anon_usage(mi, &usage);
+ if (ret) {
+ _D("getting anonymous memory usage fails");
+ return;
+ }
+
+ threshold = mi->threshold[LOWMEM_MEDIUM];
+ if (usage >= threshold)
+ memory_cgroup_medium_act(type, mi);
+ else
+ _I("anon page %u MB < medium threshold %u MB", BtoMB(usage),
+ BtoMB(threshold));
+}
+
+static Eina_Bool lowmem_press_eventfd_handler(void *data,
+ Ecore_Fd_Handler *fd_handler)
+{
+ int fd, i;
+ struct memcg_info *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");
+ 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_press_eventfd_read(fd);
+
+ for (i = 0; i < MEMCG_MAX; i++) {
+ if (!memcg_tree[i] || !memcg_tree[i]->info)
+ continue;
+ mi = memcg_tree[i]->info;
+ if (fd == mi->evfd) {
+ /* call low memory handler for this memcg */
+ if (i == MEMCG_MEMORY)
+ lowmem_press_root_cgroup_handler();
+ else
+ lowmem_press_cgroup_handler(i, mi);
+ return ECORE_CALLBACK_RENEW;
+ }
+ /* ToDo: iterate child memcgs */
+ gslist_for_each_item(iter, memcg_tree[i]->cgroups)
+ {
+ mi = (struct memcg_info *)(iter->data);
+ if (fd == mi->evfd) {
+ lowmem_press_cgroup_handler(i, mi);
+ _D("lowmem cgroup handler is called for %s",
+ mi->name);
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+ }
+
+ 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 void lowmem_press_register_eventfd(struct memcg_info *mi)
+{
+ int cgfd, pressurefd, evfd, res, sz;
+ char buf[BUF_MAX] = {0, };
+ const char *name = mi->name;
+
+ if (mi->threshold[LOWMEM_MEDIUM] == LOWMEM_THRES_INIT)
+ return;
+
+ snprintf(buf, sizeof(buf), "%s%s", name, MEMCG_EVENTFD_CONTROL);
+ cgfd = open(buf, O_WRONLY);
+ if (cgfd < 0) {
+ _E("open event_control failed");
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "%s%s", name, MEMCG_EVENTFD_MEMORY_PRESSURE);
+ pressurefd = open(buf, O_RDONLY);
+ if (pressurefd < 0) {
+ _E("open pressure control failed");
+ close(cgfd);
+ return;
+ }
+
+ /* 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;
+
+ /* pressure level*/
+ /* write event fd low level */
+ sz = snprintf(buf, sizeof(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;
+ }
+
+ _I("register event fd success for %s cgroup", name);
+ ecore_main_fd_handler_add(evfd, ECORE_FD_READ,
+ (Ecore_Fd_Cb)lowmem_press_eventfd_handler, NULL, NULL,
+ NULL);
+
+ close(cgfd);
+ close(pressurefd);
+ return;
+}
+
+static int lowmem_press_setup_eventfd(void)
+{
+ unsigned int i;
+ struct memcg_info *mi;
+ GSList *iter = NULL;
+
+ for (i = 0; i < MEMCG_MAX; i++) {
+ if (!memcg_tree[i]->use_hierarchy) {
+ lowmem_press_register_eventfd(memcg_tree[i]->info);
+ } else {
+ GSList *list = memcg_tree[i]->cgroups;
+ gslist_for_each_item(iter, list)
+ {
+ mi = (struct memcg_info *)(iter->data);
+ lowmem_press_register_eventfd(mi);
+ }
+ }
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+/* To Do: should we need lowmem_fd_start, lowmem_fd_stop ?? */
+int lowmem_init(void)
+{
+ int ret = RESOURCED_ERROR_NONE;
+
+ get_total_memory();
+
+ init_memcg_params();
+ setup_memcg_params();
+ config_parse(MEM_CONF_FILE, load_vip_config, NULL);
+ 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;
+ }
+
+ /* register threshold and event fd */
+ ret = lowmem_press_setup_eventfd();
+ if (ret) {
+ _E("eventfd setup failed");
+ return ret;
+ }
+
+ lowmem_dbus_init();
+
+ return ret;
+}
+
+static int lowmem_exit(void)
+{
+ int i;
+ for (i = 0; i < MEMCG_MAX; i++) {
+ g_slist_free_full(memcg_tree[i]->cgroups, free);
+ free(memcg_tree[i]->info);
+ free(memcg_tree[i]);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_memory_init(void *data)
+{
+ lowmem_ops = &memory_modules_ops;
+ return lowmem_init();
+}
+
+static int resourced_memory_finalize(void *data)
+{
+ return lowmem_exit();
+}
+
+static int resourced_memory_control(void *data)
+{
+ struct lowmem_data_type *l_data;
+
+ l_data = (struct lowmem_data_type *)data;
+ switch (l_data->control_type) {
+ case LOWMEM_MOVE_CGROUP:
+ lowmem_move_memcgroup((pid_t)l_data->args[0],
+ (int)l_data->args[1]);
+ break;
+ default:
+ break;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+int lowmem_memory_oom_killer(int flags)
+{
+ if (flags & OOM_FORCE)
+ return lowmem_force_oom_killer(flags, 0, MAX_FD_VICTIMS);
+ return lowmem_oom_killer_cb(MEMCG_MEMORY, memcg_root, flags, NULL);
+}
+
+int lowmem_proactive_oom_killer(int flags, char *appid)
+{
+ int count = 0;
+ unsigned int should_be_freed;
+ unsigned int before;
+#ifdef HEART_SUPPORT
+ struct heart_memory_data *md;
+#endif
+
+ before = proc_get_mem_available();
+
+ /* If low memory state, just return and kill in oom killer */
+ if (before < memcg_root->threshold[LOWMEM_MEDIUM])
+ return RESOURCED_ERROR_FAIL;
+
+#ifdef HEART_SUPPORT
+ md = heart_memory_get_data(appid, DATA_6HOUR);
+
+ if (md) {
+ unsigned int uss;
+
+ uss = KBtoMB(md->avg_uss);
+
+ free(md);
+
+ /*
+ * if launching app is predicted to consume all memory,
+ * free memory up to leave threshold after launching the app.
+ */
+ if (before <= uss) {
+ should_be_freed = memcg_root->threshold_leave + uss;
+ lowmem_force_oom_killer(OOM_FORCE, should_be_freed, num_max_victims);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ unsigned int after = before - uss;
+ _D("available after launch = %u MB, available = %u MB, uss = %u MB",
+ after, before, uss);
+
+ /*
+ * after launching app, ensure that available memory is
+ * above threshold_leave
+ */
+ if (after >= memcg_root->threshold[LOWMEM_MEDIUM])
+ return RESOURCED_ERROR_FAIL;
+
+ should_be_freed = memcg_root->threshold_leave +
+ THRESHOLD_MARGIN - after;
+ _D("run history based proactive killer, should_be_freed = %u MB",
+ should_be_freed);
+ lowmem_force_oom_killer(OOM_FORCE, should_be_freed, num_max_victims);
+
+ return RESOURCED_ERROR_NONE;
+ }
+#endif
+
+ /*
+ * When there is no history data for the launching app but it is
+ * indicated as PROC_LARGEMEMORY, run oom killer based on dynamic
+ * threshold.
+ */
+ if (!(flags & PROC_LARGEMEMORY))
+ return RESOURCED_ERROR_FAIL;
+ /*
+ * run proactive oom killer only when available is larger than
+ * dynamic process threshold
+ */
+ if (!proactive_threshold || before >= proactive_threshold)
+ return RESOURCED_ERROR_FAIL;
+
+ /*
+ * free THRESHOLD_MARGIN more than real should be freed,
+ * because launching app is consuming up the memory.
+ */
+ should_be_freed = proactive_leave - before + THRESHOLD_MARGIN;
+ _D("run threshold based proactive killer, should_be_freed = %u MB",
+ should_be_freed);
+
+ count = lowmem_force_oom_killer(OOM_FORCE, should_be_freed, num_max_victims);
+ _D("kill %d victim total", count);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+void lowmem_change_memory_state(int state, int force)
+{
+ int mem_state;
+
+ if (force) {
+ mem_state = state;
+ } else {
+ unsigned int available = proc_get_mem_available();
+ mem_state = check_mem_state(available);
+ _D("available = %u, mem_state = %s", available,
+ convert_memstate_to_str(mem_state));
+ }
+
+ switch (mem_state) {
+ case LOWMEM_NORMAL:
+ normal_act();
+ break;
+ case LOWMEM_SWAP:
+ swap_act();
+ break;
+ case LOWMEM_LOW:
+ low_act();
+ break;
+ case LOWMEM_MEDIUM:
+ medium_act();
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void lowmem_memcg_set_threshold(int type, int level, int value)
+{
+ memcg_tree[type]->info->threshold[level] = value;
+}
+
+void lowmem_memcg_set_leave_threshold(int type, int value)
+{
+ memcg_tree[type]->info->threshold_leave = value;
+}
+
+unsigned long lowmem_get_ktotalram(void)
+{
+ return ktotalram;
+}
+
+int lowmem_get_memcg(enum memcg_type type, struct memcg **memcg_ptr)
+{
+
+ if (memcg_ptr == NULL || memcg_tree == NULL || type >= MEMCG_MAX)
+ return RESOURCED_ERROR_FAIL;
+
+ *memcg_ptr = memcg_tree[type];
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops memory_modules_ops = {
+ .priority = MODULE_PRIORITY_EARLY,
+ .name = "lowmem",
+ .init = resourced_memory_init,
+ .exit = resourced_memory_finalize,
+ .control = resourced_memory_control,
+};
+
+MODULE_REGISTER(&memory_modules_ops)
--- /dev/null
+PROJECT(memps)
+
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+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")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${MEMPS_SOURCE_DIR}/memps.c)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} resourced_shared)
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE)
--- /dev/null
+/*
+ Copyright (c) 2000 - 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.
+
+ 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
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.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 <getopt.h>
+
+#include "file-helper.h"
+#include "smaps.h"
+#include "procfs.h"
+#include "util.h"
+#include "trace.h"
+#include "cgroup.h"
+
+#define STR_SGX_PATH "/dev/pvrsrvkm"
+#define STR_3D_PATH1 "/dev/mali"
+#define STR_3D_PATH2 "/dev/kgsl-3d0"
+#define STR_DRM_PATH1 "/drm mm object (deleted)"
+#define STR_DRM_PATH2 "/dev/dri/card0"
+#define MEMCG_PATH "/sys/fs/cgroup/memory"
+#define ZRAM_USED_PATH "/sys/block/zram0/mem_used_total"
+
+#define BUF_MAX (BUFSIZ) /* most optimal for libc::stdio */
+#define BUF_INC_SIZE (512 << 10) /* maximal SMAPS I saw 2 MB */
+
+typedef struct geminfo geminfo;
+typedef struct trib_mapinfo trib_mapinfo;
+
+/* classify normal, graphic and other devices memory */
+struct trib_mapinfo {
+ unsigned shared_clean;
+ unsigned shared_dirty;
+ unsigned private_clean;
+ unsigned private_dirty;
+ unsigned shared_clean_pss;
+ unsigned shared_dirty_pss;
+ unsigned swap;
+ unsigned rss;
+ unsigned pss;
+ unsigned size;
+ unsigned graphic_3d;
+ unsigned gem_rss;
+ unsigned gem_pss;
+ unsigned peak_rss;
+ unsigned other_devices;
+ unsigned gem_mmap;
+};
+
+struct geminfo {
+ geminfo *next;
+ unsigned int tgid;
+ unsigned rss_size;
+ unsigned pss_size;
+ unsigned hcount;
+};
+
+static bool arg_sum = false;
+static bool arg_all = false;
+static bool arg_rss = false;
+static bool arg_verbose = false;
+static char *arg_file = NULL;
+
+
+static unsigned get_peak_rss(unsigned int pid)
+{
+ static const char field[] = "VmHWM:";
+ char tmp[128];
+ char* line;
+ char* value;
+
+ snprintf(tmp, sizeof(tmp), "/proc/%d/status", pid);
+ line = cread(tmp);
+ if (line == NULL) {
+ _E("cannot open %s", tmp);
+ return 0;
+ }
+
+ value = strstr(line, field);
+ if (value) {
+ value += sizeof(field);
+ return strtoul(value, NULL, 10);
+ }
+
+ return 0;
+}
+#define NUM_GEM_FIELD 6
+
+static geminfo *read_geminfo(FILE *fp)
+{
+ geminfo *tgeminfo;
+ char line[BUF_MAX];
+ unsigned int pid, tgid, handle, refcount, hcount;
+ unsigned gem_size;
+
+ if (fgets(line, BUF_MAX, fp) == NULL)
+ return NULL;
+
+ if (sscanf(line, "%d %d %d %d %d 0x%x", &pid, &tgid,
+ &handle, &refcount, &hcount, &gem_size) != NUM_GEM_FIELD)
+ return NULL;
+
+ if (hcount == 0)
+ return NULL;
+
+ tgeminfo = malloc(sizeof(geminfo));
+ if (tgeminfo == NULL)
+ return NULL;
+
+ tgeminfo->tgid = tgid;
+ tgeminfo->hcount = hcount;
+ tgeminfo->rss_size = BYTE_TO_KBYTE(gem_size);
+ tgeminfo->pss_size = BYTE_TO_KBYTE(gem_size/tgeminfo->hcount);
+
+ return tgeminfo;
+}
+
+
+static geminfo *load_geminfo(void)
+{
+ geminfo *ginfo;
+ geminfo *gilist = NULL;
+ _cleanup_fclose_ FILE *drm_fp = NULL;
+ char line[BUF_MAX];
+
+ drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
+
+ if (drm_fp == NULL) {
+ _E("cannot open /sys/kernel/debug/dri/0/gem_info");
+ return NULL;
+ }
+
+ if (fgets(line, BUF_MAX, drm_fp) == NULL)
+ return NULL;
+ else {
+ /* we should count a number of whitespace separated fields */
+ int in_field = (line[0] && !isblank(line[0]));
+ unsigned int size = (unsigned)in_field;
+ const char* ptr = &line[1];
+
+ /* sscanf() was used in original code, so number of fields */
+ /* in string is expected to be at least NUM_GEM_FIELD */
+ while (*ptr && size < NUM_GEM_FIELD) {
+ if (isblank(*ptr++)) {
+ if (in_field) {
+ /* end of field */
+ in_field = 0;
+ }
+ } else {
+ if (!in_field) {
+ /* next field started */
+ in_field = 1;
+ size++;
+ }
+ }
+ } /* while */
+
+ if (size != NUM_GEM_FIELD)
+ return NULL;
+ }
+
+ while ((ginfo = read_geminfo(drm_fp)) != NULL) {
+ if (gilist && ginfo->tgid == gilist->tgid) {
+ gilist->pss_size += ginfo->pss_size;
+ gilist->rss_size += ginfo->rss_size;
+ free(ginfo);
+ continue;
+ }
+ ginfo->next = gilist;
+ gilist = ginfo;
+ }
+
+ return gilist;
+}
+
+static unsigned total_gem_memory(void)
+{
+ _cleanup_fclose_ FILE *gem_fp = NULL;
+ unsigned total_gem_mem = 0;
+ unsigned name, size, handles, refcount;
+ char line[BUF_MAX];
+
+ gem_fp = fopen("/proc/dri/0/gem_names", "r");
+ if(gem_fp == NULL) {
+ _E("cannot open /proc/dir/0/gem_names");
+ return 0;
+ }
+
+ if (fgets(line, BUF_MAX, gem_fp) == NULL)
+ return 0;
+
+ while (fgets(line, BUF_MAX, gem_fp) != NULL)
+ if (sscanf(line, "%d %d %d %d\n",
+ &name, &size, &handles, &refcount) == 4)
+ total_gem_mem += size;
+
+ return total_gem_mem;
+}
+
+/**
+ * @desc Provides usage in bytes for provided memory cgroup. Works
+ * with/without swap accounting.
+ *
+ * @param memcg_path[in] Full path to memory cgroup
+ * @param swap[in] Boolean value for deciding if account usage with swap
+ * @return current cgroup usage in bytes or 0 on error
+ */
+static unsigned int get_memcg_usage(const char *memcg_path, bool swap)
+{
+ int ret;
+ unsigned int usage;
+
+ if (swap) {
+ ret = cgroup_read_node(memcg_path,
+ "/memory.memsw.usage_in_bytes", &usage);
+ } else {
+ ret = cgroup_read_node(memcg_path, "/memory.usage_in_bytes",
+ &usage);
+ }
+
+ if (ret != RESOURCED_ERROR_NONE)
+ usage = 0;
+
+ return usage;
+}
+
+static void get_memcg_info(void)
+{
+ char buf[PATH_MAX];
+ _cleanup_closedir_ DIR *pdir = NULL;
+ struct dirent *entry;
+ struct stat path_stat;
+ long usage_swap;
+ unsigned long usage, usage_with_swap;
+
+ _I("====================================================================");
+ _I("MEMORY CGROUPS USAGE INFO");
+
+ pdir = opendir(MEMCG_PATH);
+ if (pdir == NULL) {
+ _E("cannot read directory %s", MEMCG_PATH);
+ return;
+ }
+
+ while ((entry = readdir(pdir)) != NULL) {
+ sprintf(buf, "%s/%s", MEMCG_PATH, entry->d_name);
+ /* If can't stat then ignore */
+ if (stat(buf, &path_stat) != 0)
+ continue;
+
+ /* If it's not directory or it's parent path then ignore */
+ if (!(S_ISDIR(path_stat.st_mode) &&
+ strcmp(entry->d_name, "..")))
+ continue;
+
+ usage = get_memcg_usage(buf, false);
+ usage_with_swap = get_memcg_usage(buf, true);
+ /* It is posible by rounding errors to get negative value */
+ usage_swap = usage_with_swap - usage;
+ if (usage_swap < 0)
+ usage_swap = 0;
+
+ /* Case of root cgroup in hierarchy */
+ if (!strcmp(entry->d_name, "."))
+ _I("%13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB) \n",
+ MEMCG_PATH, BYTE_TO_MBYTE(usage),
+ BYTE_TO_KBYTE(usage),
+ BYTE_TO_MBYTE(usage_with_swap),
+ BYTE_TO_KBYTE(usage_with_swap),
+ BYTE_TO_MBYTE(usage_swap),
+ BYTE_TO_KBYTE(usage_swap));
+ else
+ _I("memcg: %13s Mem %3ld MB (%6ld kB), Mem+Swap %3ld MB (%6ld kB), Swap %3ld MB (%6ld kB)",
+ entry->d_name, BYTE_TO_MBYTE(usage),
+ BYTE_TO_KBYTE(usage),
+ BYTE_TO_MBYTE(usage_with_swap),
+ BYTE_TO_KBYTE(usage_with_swap),
+ BYTE_TO_MBYTE(usage_swap),
+ BYTE_TO_KBYTE(usage_swap));
+
+ }
+}
+
+static void get_mem_info(void)
+{
+ struct meminfo mi;
+ int r;
+
+ unsigned int free = 0;
+ unsigned int total_mem = 0, available = 0, used;
+ unsigned int swap_total = 0, swap_free = 0, zram_used, swap_used;
+ unsigned int used_ratio;
+
+ r = proc_get_meminfo(&mi,
+ (MEMINFO_MASK_MEM_TOTAL |
+ MEMINFO_MASK_MEM_FREE |
+ MEMINFO_MASK_MEM_AVAILABLE |
+ MEMINFO_MASK_CACHED |
+ MEMINFO_MASK_SWAP_TOTAL |
+ MEMINFO_MASK_SWAP_FREE));
+ if (r < 0) {
+ _E("Failed to get meminfo");
+ return;
+ }
+
+ total_mem = mi.value[MEMINFO_ID_MEM_TOTAL];
+ free = mi.value[MEMINFO_ID_MEM_FREE];
+ available = mi.value[MEMINFO_ID_MEM_AVAILABLE];
+ swap_total = mi.value[MEMINFO_ID_SWAP_TOTAL];
+ swap_free = mi.value[MEMINFO_ID_SWAP_FREE];
+
+ if (total_mem == 0)
+ return;
+
+ used = total_mem - available;
+ used_ratio = used * 100 / total_mem;
+ swap_used = swap_total - swap_free;
+
+ if (fread_uint(ZRAM_USED_PATH, &zram_used) != RESOURCED_ERROR_NONE)
+ zram_used = 0;
+
+ _I("====================================================================");
+
+
+ _I( "Total RAM size: \t%15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(total_mem), total_mem);
+
+ _I( "Used (Mem+Reclaimable): %15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(total_mem - free), total_mem - free);
+
+ _I( "Used (Mem+Swap): \t%15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(used), used);
+
+ _I( "Used (Mem): \t\t%15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(used), used);
+
+ _I( "Used (Swap): \t\t%15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(swap_used), swap_used);
+
+ _I( "Used (Zram block device): %13d MB( %6d kB)",
+ BYTE_TO_MBYTE(zram_used), BYTE_TO_KBYTE(zram_used));
+
+ _I( "Used Ratio: \t\t%15d %%", used_ratio);
+
+ _I( "Mem Free:\t\t%15d MB( %6d kB)",
+ KBYTE_TO_MBYTE(free), free);
+
+ _I( "Available (Free+Reclaimable):%10d MB( %6d kB)",
+ KBYTE_TO_MBYTE(available), available);
+}
+
+static int get_tmpfs_info(void)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ char line[BUF_MAX];
+ char tmpfs_mp[NAME_MAX]; /* tmpfs mount point */
+ struct statfs tmpfs_info;
+
+ fp = fopen("/etc/mtab", "r");
+ if (fp == NULL)
+ return -1;
+
+ _I("====================================================================");
+ _I( "TMPFS INFO");
+
+ while (fgets(line, BUF_MAX, fp) != NULL) {
+ if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
+ statfs(tmpfs_mp, &tmpfs_info);
+ _I("tmpfs %16s Total %8ld KB, Used %8ld, Avail %8ld",
+ tmpfs_mp,
+ /* 1 block is 4 KB */
+ tmpfs_info.f_blocks * 4,
+ (tmpfs_info.f_blocks - tmpfs_info.f_bfree) * 4,
+ tmpfs_info.f_bfree * 4);
+ }
+ }
+
+ return 0;
+}
+
+static geminfo *find_geminfo(unsigned int tgid, geminfo *gilist)
+{
+ geminfo *gi;
+ for (gi = gilist; gi; ) {
+ if (gi->tgid == tgid)
+ return gi;
+
+ gi = gi->next;
+ }
+ return NULL;
+}
+
+static void init_trib_mapinfo(trib_mapinfo *tmi)
+{
+ if (!tmi)
+ return;
+ tmi->shared_clean = 0;
+ tmi->shared_dirty = 0;
+ tmi->private_clean = 0;
+ tmi->private_dirty = 0;
+ tmi->swap = 0;
+ tmi->shared_clean_pss = 0;
+ tmi->shared_dirty_pss = 0;
+ tmi->rss = 0;
+ tmi->pss = 0;
+ tmi->size = 0;
+ tmi->graphic_3d = 0;
+ tmi->gem_rss = 0;
+ tmi->gem_pss = 0;
+ tmi->peak_rss = 0;
+ tmi->other_devices = 0;
+ tmi->gem_mmap = 0;
+}
+
+static int
+get_trib_mapinfo(unsigned int tgid, struct smaps *maps,
+ geminfo *gilist, trib_mapinfo *result)
+
+{
+ geminfo *gi;
+
+ int i;
+
+ if (!result)
+ return -EINVAL;
+
+ init_trib_mapinfo(result);
+
+ for (i = 0; i < maps->n_map; i++) {
+ if (strstr(maps->maps[i]->name, STR_SGX_PATH)) {
+ result->graphic_3d += maps->maps[i]->value[SMAPS_ID_PSS];
+ } else if (strstr(maps->maps[i]->name, STR_3D_PATH1) ||
+ strstr(maps->maps[i]->name, STR_3D_PATH2)) {
+ result->graphic_3d += maps->maps[i]->value[SMAPS_ID_SIZE];
+ } else if (maps->maps[i]->value[SMAPS_ID_RSS] != 0 &&
+ maps->maps[i]->value[SMAPS_ID_PSS] == 0 &&
+ maps->maps[i]->value[SMAPS_ID_SHARED_CLEAN] == 0 &&
+ maps->maps[i]->value[SMAPS_ID_SHARED_DIRTY] == 0 &&
+ maps->maps[i]->value[SMAPS_ID_PRIVATE_CLEAN] == 0 &&
+ maps->maps[i]->value[SMAPS_ID_PRIVATE_DIRTY] == 0 &&
+ maps->maps[i]->value[SMAPS_ID_SWAP] == 0) {
+ result->other_devices += maps->maps[i]->value[SMAPS_ID_SIZE];
+ } else if (!strncmp(maps->maps[i]->name, STR_DRM_PATH1, sizeof(STR_DRM_PATH1)) ||
+ !strncmp(maps->maps[i]->name, STR_DRM_PATH2, sizeof(STR_DRM_PATH2))) {
+ result->gem_mmap += maps->maps[i]->value[SMAPS_ID_RSS];
+ } else {
+ result->shared_clean += maps->maps[i]->value[SMAPS_ID_SHARED_CLEAN];
+ result->shared_dirty += maps->maps[i]->value[SMAPS_ID_SHARED_DIRTY];
+ result->private_clean += maps->maps[i]->value[SMAPS_ID_PRIVATE_CLEAN];
+ result->private_dirty += maps->maps[i]->value[SMAPS_ID_PRIVATE_DIRTY];
+ result->swap += maps->maps[i]->value[SMAPS_ID_SWAP];
+ result->rss += maps->maps[i]->value[SMAPS_ID_RSS];
+ result->pss += maps->maps[i]->value[SMAPS_ID_PSS];
+ result->size += maps->maps[i]->value[SMAPS_ID_SIZE];
+
+ if(maps->maps[i]->value[SMAPS_ID_SHARED_CLEAN] != 0)
+ result->shared_clean_pss += maps->maps[i]->value[SMAPS_ID_PSS];
+ else if (maps->maps[i]->value[SMAPS_ID_SHARED_DIRTY] != 0)
+ result->shared_dirty_pss += maps->maps[i]->value[SMAPS_ID_PSS];
+ }
+ }
+
+ result->peak_rss = get_peak_rss(tgid);
+ if (result->peak_rss < result->rss)
+ result->peak_rss = result->rss;
+ if (result->gem_mmap > 0)
+ result->peak_rss -= result->gem_mmap;
+
+ gi = find_geminfo(tgid, gilist);
+ if (gi != NULL) {
+ result->gem_rss = gi->rss_size;
+ result->gem_pss = gi->pss_size;
+ }
+
+ return 0;
+}
+
+static int get_cmdline(unsigned int pid, char *cmdline)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ char buf[NAME_MAX] = {0, };
+
+ snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+ fp = fopen(buf, "r");
+ if (fp == 0) {
+ _E("cannot file open %s", buf);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return fscanf(fp, "%s", cmdline);
+}
+
+static int get_oomscoreadj(unsigned int pid)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ char tmp[256];
+ int oomadj_val;
+
+ snprintf(tmp, sizeof(tmp), "/proc/%d/oom_score_adj", pid);
+ fp = fopen(tmp, "r");
+
+ if (fp == NULL) {
+ oomadj_val = -50;
+ return oomadj_val;
+ }
+ if (fgets(tmp, sizeof(tmp), fp) == NULL) {
+ oomadj_val = -100;
+ return oomadj_val;
+ }
+
+ oomadj_val = atoi(tmp);
+
+ return oomadj_val;
+}
+
+static void get_rss(pid_t pid, unsigned int *result)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ char proc_path[PATH_MAX];
+ int rss = 0;
+
+ *result = 0;
+
+ snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
+ fp = fopen(proc_path, "r");
+ if (fp == NULL)
+ return;
+
+ if (fscanf(fp, "%*s %d", &rss) < 1)
+ return;
+
+ /* convert page to Kb */
+ *result = rss * 4;
+ return;
+}
+
+static void show_rss(void)
+{
+ _cleanup_closedir_ DIR *pDir = NULL;
+ struct dirent curdir;
+ struct dirent *result;
+ pid_t pid;
+ char cmdline[PATH_MAX];
+ _cleanup_fclose_ FILE *output_file = NULL;
+ int oom_score_adj;
+ unsigned int rss;
+ int ret;
+
+ pDir = opendir("/proc");
+ if (pDir == NULL) {
+ _E("cannot read directory /proc.");
+ return;
+ }
+
+ _I(" PID RSS OOM_SCORE COMMAND");
+
+ while (!(ret = readdir_r(pDir, &curdir, &result)) && result != NULL) {
+ pid = atoi(curdir.d_name);
+ if (pid < 1 || pid > 32768 || pid == getpid())
+ continue;
+
+ if (get_cmdline(pid, cmdline) < 0)
+ continue;
+ get_rss(pid, &rss);
+ oom_score_adj = get_oomscoreadj(pid);
+
+ _I("%8d %8u %8d %s",
+ pid,
+ rss,
+ oom_score_adj,
+ cmdline);
+
+
+ } /* end of while */
+
+ get_tmpfs_info();
+ get_mem_info();
+
+ return;
+}
+
+static int show_map_all_new(void)
+{
+ _cleanup_closedir_ DIR *pDir = NULL;
+ struct dirent curdir;
+ struct dirent *result;
+ unsigned int pid;
+ _cleanup_free_ geminfo *glist = NULL;
+ unsigned total_pss = 0;
+ unsigned total_private = 0;
+ unsigned total_private_code = 0;
+ unsigned total_private_data = 0;
+ unsigned total_shared_code = 0;
+ unsigned total_shared_data = 0;
+ unsigned total_shared_code_pss = 0;
+ unsigned total_shared_data_pss = 0;
+ unsigned total_swap = 0;
+ unsigned total_rss = 0;
+ unsigned total_graphic_3d = 0;
+ unsigned total_gem_rss = 0;
+ unsigned total_gem_pss = 0;
+ unsigned total_peak_rss = 0;
+ unsigned total_allocated_gem = 0;
+ trib_mapinfo tmi;
+ char cmdline[PATH_MAX];
+ _cleanup_fclose_ FILE *output_file = NULL;
+
+ int r;
+
+ pDir = opendir("/proc");
+ if (pDir == NULL) {
+ _E("cannot read directory /proc.");
+ return 0;
+ }
+
+ glist = load_geminfo();
+
+ if (!arg_sum) {
+ if (arg_verbose)
+ _I(" PID S(CODE) S(DATA) P(CODE) P(DATA)"
+ " PEAK PSS 3D"
+ " GEM(PSS) GEM(RSS) SWAP"
+ " OOM_SCORE_ADJ COMMAND");
+ else
+ _I(" PID CODE DATA PEAK PSS"
+ " 3D GEM(PSS) SWAP COMMAND");
+ }
+
+ while (!(r = readdir_r(pDir, &curdir, &result)) && result != NULL) {
+ _cleanup_smaps_free_ struct smaps *maps = NULL;
+ char *base_name = NULL;
+
+ pid = atoi(curdir.d_name);
+ if (pid < 1 || pid > 32768 || pid == getpid())
+ continue;
+
+ if (get_cmdline(pid, cmdline) < 0)
+ continue;
+
+ base_name = basename(cmdline);
+ if (base_name && !strcmp(base_name, "mem-stress"))
+ continue;
+
+ r = smaps_get(pid, &maps, SMAPS_MASK_DEFAULT);
+ if (r < 0) {
+ _E("cannot get smaps of pid %d", pid);
+ continue;
+ }
+
+ /* get classified map info */
+ get_trib_mapinfo(pid, maps, glist, &tmi);
+
+ if (!arg_sum) {
+ if (arg_verbose)
+ _I("%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
+ " %8d \t\t%s",
+ pid,
+ tmi.shared_clean, tmi.shared_dirty,
+ tmi.private_clean, tmi.private_dirty,
+ tmi.peak_rss, tmi.pss, tmi.graphic_3d,
+ tmi.gem_pss, tmi.gem_rss, tmi.swap,
+ get_oomscoreadj(pid), cmdline);
+ else
+ _I("%8d %8d %8d %8d %8d %8d %8d %8d %s",
+ pid,
+ tmi.shared_clean +
+ tmi.private_clean,
+ tmi.shared_dirty + tmi.private_dirty,
+ tmi.peak_rss,
+ tmi.pss,
+ tmi.graphic_3d,
+ tmi.gem_pss,
+ tmi.swap, cmdline);
+
+ if (tmi.other_devices != 0)
+ _I("%s(%d) %d KB may mapped by device(s).",
+ cmdline, pid, tmi.other_devices);
+ }
+
+ total_private += (tmi.private_clean + tmi.private_dirty);
+ total_pss += tmi.pss;
+ total_rss += tmi.rss;
+ total_graphic_3d += tmi.graphic_3d;
+ total_gem_rss += tmi.gem_rss;
+ total_gem_pss += tmi.gem_pss;
+ total_private_code += tmi.private_clean;
+ total_private_data += tmi.private_dirty;
+ total_swap += tmi.swap;
+ total_shared_code += tmi.shared_clean;
+ total_shared_data += tmi.shared_dirty;
+ total_peak_rss += tmi.peak_rss;
+
+ total_shared_code_pss += tmi.shared_clean_pss;
+ total_shared_data_pss += tmi.shared_dirty_pss;
+
+ } /* end of while */
+
+ total_allocated_gem = BYTE_TO_KBYTE(total_gem_memory());
+ _I("==============================================="
+ "===============================================");
+ if (arg_verbose) {
+ _I("TOTAL: S(CODE) S(DATA) P(CODE) P(DATA)"
+ " PEAK PSS 3D "
+ "GEM(PSS) GEM(RSS) GEM(ALLOC) SWAP TOTAL(KB)");
+ _I(" %8d %8d %8d %8d %8d %8d %8d"
+ " %8d %8d %8d %8d %8d",
+ total_shared_code, total_shared_data,
+ total_private_code, total_private_data,
+ total_peak_rss, total_pss, total_graphic_3d,
+ total_gem_pss, total_gem_rss,
+ total_allocated_gem, total_swap,
+ total_pss + total_graphic_3d +
+ total_allocated_gem);
+ } else {
+ _I("TOTAL: CODE DATA PEAK PSS "
+ "3D GEM(PSS) GEM(ALLOC) TOTAL(KB)");
+ _I(" %8d %8d %8d %8d %8d %8d %7d %8d %8d",
+ total_shared_code + total_private_code,
+ total_shared_data + total_private_data,
+ total_peak_rss, total_pss,
+ total_graphic_3d, total_gem_pss,
+ total_allocated_gem, total_swap,
+ total_pss + total_graphic_3d +
+ total_allocated_gem);
+
+ }
+
+ if (arg_verbose)
+ _I("* S(CODE): shared clean memory, it includes"
+ " duplicated memory\n"
+ "* S(DATA): shared dirty memory, it includes"
+ " duplicated memory\n"
+ "* P(CODE): private clean memory\n"
+ "* P(DATA): private dirty memory\n"
+ "* PEAK: peak memory usage of S(CODE) + S(DATA)"
+ " + P(CODE) + P(DATA)\n"
+ "* PSS: Proportional Set Size\n"
+ "* 3D: memory allocated by GPU driver\n"
+ "* GEM(PSS): GEM memory devided by # of sharers\n"
+ "* GEM(RSS): GEM memory including duplicated memory\n"
+ "* GEM(ALLOC): sum of unique gem memory in the system\n"
+ "* TOTAL: PSS + 3D + GEM(ALLOC)");
+ else
+ _I("* CODE: shared and private clean memory\n"
+ "* DATA: shared and private dirty memory\n"
+ "* PEAK: peak memory usage of CODE + DATA\n"
+ "* PSS: Proportional Set Size\n"
+ "* 3D: memory allocated by GPU driver\n"
+ "* GEM(PSS): GEM memory deviced by # of sharers\n"
+ "* GEM(ALLOC): sum of unique GEM memory in the system\n"
+ "* TOTAL: PSS + 3D + GEM(ALLOC)");
+
+ get_tmpfs_info();
+ get_memcg_info();
+ get_mem_info();
+
+ return 1;
+}
+
+static int show_map_new(int pid)
+{
+ _cleanup_smaps_free_ struct smaps *maps = NULL;
+ int r, i;
+
+ maps = new0(struct smaps, 1);
+ if (!maps)
+ return -ENOMEM;
+
+ r = smaps_get(pid, &maps, SMAPS_MASK_DEFAULT);
+ if (r < 0) {
+ _E("cannot get smaps of pid %d", pid);
+ return r;
+ }
+
+ if (arg_sum) {
+ _I(" S(CODE) S(DATA) P(CODE) P(DATA) PSS");
+ _I("-------- -------- -------------------"
+ "------------------");
+
+ _I("%8d %8d %8d %8d %8d %18d",
+ maps->sum[SMAPS_ID_SHARED_CLEAN],
+ maps->sum[SMAPS_ID_SHARED_DIRTY],
+ maps->sum[SMAPS_ID_PRIVATE_CLEAN],
+ maps->sum[SMAPS_ID_PRIVATE_DIRTY],
+ maps->sum[SMAPS_ID_SWAP],
+ maps->sum[SMAPS_ID_PSS]);
+ } else {
+ _I(" S(CODE) S(DATA) P(CODE) P(DATA) ADDR(start-end)"
+ "OBJECT NAME");
+ _I("-------- -------- -------- -------- -----------------"
+ "------------------------------");
+ for (i = 0; i < maps->n_map; i++)
+ _I("%8d %8d %8d %8d %08x-%08x %s",
+ maps->maps[i]->value[SMAPS_ID_SHARED_CLEAN],
+ maps->maps[i]->value[SMAPS_ID_SHARED_DIRTY],
+ maps->maps[i]->value[SMAPS_ID_PRIVATE_CLEAN],
+ maps->maps[i]->value[SMAPS_ID_PRIVATE_DIRTY],
+ maps->maps[i]->start,
+ maps->maps[i]->end,
+ maps->maps[i]->name);
+ }
+
+ return 1;
+}
+
+static void memps_show_help(void)
+{
+ _E("memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
+ "\t-s = sum (show only sum of each)\n"
+ "\t-f = all (show all processes via output file)\n"
+ "\t-a = all (show all processes)\n"
+ "\t-v = verbos (show all processes in detail)");
+}
+
+static int memps_parse_args(int argc, char *argv[])
+{
+ static const struct option long_options[] = {
+ {"sum", no_argument, NULL, 's' },
+ {"file", required_argument, NULL, 'f' },
+ {"all", no_argument, NULL, 'a' },
+ {"rss", no_argument, NULL, 'r' },
+ {"verbose", no_argument, NULL, 'v' },
+ {"help", no_argument, NULL, 'h' },
+ {0, 0, 0, 0}
+ };
+ int c = 0;
+
+ while (c != -1) {
+ c = getopt_long(argc, argv, "sf:arv", long_options, NULL);
+
+ switch (c) {
+ case 's':
+ arg_sum = true;
+ break;
+ case 'f':
+ arg_file = optarg;
+ break;
+ case 'a':
+ arg_all = true;
+ break;
+ case 'r':
+ arg_rss = true;
+ break;
+ case 'v':
+ arg_verbose = true;
+ break;
+ case 'h':
+ break;
+ case '?':
+ return -EINVAL;
+ }
+ }
+
+ if (arg_all || arg_file || arg_verbose || arg_rss) {
+ if ( optind != argc) {
+ _E("Invalid arguments");
+ return -EINVAL;
+ }
+ } else {
+ if (optind + 1 != argc) {
+ _E("Invalid arguments");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+
+ r = memps_parse_args(argc, argv);
+ if (r < 0) {
+ memps_show_help();
+ return EXIT_FAILURE;
+ }
+
+ if (arg_file)
+ log_open(LOG_TYPE_FILE, arg_file);
+ else
+ log_open(LOG_TYPE_STANDARD, NULL);
+
+ if (arg_all || arg_verbose)
+ show_map_all_new();
+ else if (arg_rss)
+ show_rss();
+ else
+ show_map_new(atoi(argv[optind]));
+
+ log_close();
+
+ return 0;
+}
--- /dev/null
+network_db_version="`sqlite3 /opt/usr/dbspace/.resourced-datausage.db "PRAGMA user_version;"`"
+
+if [ 0"$network_db_version" -eq 1 ]
+then
+ echo "user_version PRAGMA is 1. No need to run this script"
+ exit 1
+fi
+
+sqlite3 /opt/usr/dbspace/.resourced-datausage.db "
+PRAGMA journal_mode=PERSIST;
+DROP TABLE IF EXISTS fota;
+ALTER TABLE statistics RENAME to fota;
+CREATE TABLE IF NOT EXISTS statistics (
+ binpath TEXT,
+ received BIGINT,
+ sent BIGINT,
+ time_stamp BIGINT,
+ iftype INT,
+ is_roaming INT,
+ hw_net_protocol_type INT,
+ ifname TEXT,
+ reserved TEXT,
+ imsi TEXT,
+ ground INT DEFAULT 0,
+ PRIMARY KEY (binpath, time_stamp, iftype, imsi)
+);
+INSERT INTO statistics (binpath, received, sent, time_stamp, iftype, is_roaming,
+hw_net_protocol_type, ifname, reserved, imsi) SELECT binpath, received, sent, time_stamp, iftype, is_roaming,
+hw_net_protocol_type, ifname, reserved, imsi from fota;
+DROP TABLE IF EXISTS fota;
+"
+sqlite3 /opt/usr/dbspace/.resourced-datausage.db "
+PRAGMA journal_mode=PERSIST;
+DROP TABLE IF EXISTS fota;
+ALTER TABLE quotas RENAME to fota;
+CREATE TABLE IF NOT EXISTS quotas (
+ binpath TEXT,
+ sent_quota BIGINT,
+ rcv_quota BIGINT,
+ snd_warning_threshold INT,
+ rcv_warning_threshold INT,
+ time_period BIGINT,
+ start_time BIGINT,
+ iftype INT,
+ roaming INT,
+ reserved TEXT,
+ imsi TEXT,
+ ground INT DEFAULT 0,
+ PRIMARY KEY(binpath, iftype, roaming, imsi, ground)
+);
+INSERT INTO quotas (binpath, sent_quota, rcv_quota, snd_warning_threshold, rcv_warning_threshold, time_period,
+start_time, iftype, roaming, reserved, imsi) SELECT binpath, sent_quota, rcv_quota, snd_warning_threshold, rcv_warning_threshold, time_period,
+start_time, iftype, roaming, reserved, imsi from fota;
+DROP TABLE IF EXISTS fota;
+"
+
+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,
+imsi TEXT DEFAULT 'noneimsi',
+PRIMARY KEY (binpath, iftype, ifname, quota_id, imsi)
+);
+INSERT INTO restrictions (binpath, rcv_limit, send_limit, iftype, rst_state, quota_id,
+roaming, reserved, ifname) SELECT binpath, rcv_limit, send_limit, iftype, rst_state, quota_id,
+roaming, reserved, ifname from fota;
+
+INSERT INTO restrictions VALUES('com.samsung.easysignup', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.osmeta.runtime', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.osmeta.runtime.service', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.email', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-composer-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-viewer-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-conversation-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-mailbox-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-account-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-filter-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-block-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-locker-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('email-setting-efl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.email-misc_worker', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.email-record-video-icon', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.samsungaccount', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.samsungaccount.samsungaccountservice', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.samsungaccount.samsungaccountpushefl', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.samsungaccount.samsungaccountupdate', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.special-day-app', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.special-day-widget', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.tizenstore.billingagent', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.inapppurchase', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.inapppurchase.iapclient', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.inapppurchase.iapservice', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.tizenstore', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.tizenstoreservice', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.videos-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.video-player-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('org.tizen.webcontainer', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.themestore', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('ACL111OMWW.AclService', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('ACL111OMWW.AclManager', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('ACL111OMWW.AclAudioProxyService', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.gallery-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('gallery-efl-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.gallery-lite.appcontrol', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.image-viewer.appcontrol.slideshow', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.image-viewer', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.image-viewer-subapp', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.image-viewer-subapp-single', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('image-viewer-efl-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.gallery-lite.dbox', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.music-player-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.sound-player-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.music-chooser-lite', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.music-player-lite.widget', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+INSERT INTO restrictions VALUES('com.samsung.cloud-content-sync', 0, 0, 1, 3, 0, 2, '', 'seth_w0', 'noneimsi');
+DROP TABLE IF EXISTS fota;
+"
--- /dev/null
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+SET(PROJECT ${RESOURCED})
+PROJECT(${PROJECT})
+
+IF("${NETWORK_MODULE}" STREQUAL "OFF")
+ INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR}
+ ${INCLUDE_COMMON_DIR})
+ SET(SOURCES ${NETWORK_SOURCE_DIR}/join-dummy.c
+ ${NETWORK_SOURCE_DIR}/network-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(FILES ${INCLUDE_PUBLIC_DIR}/data_usage.h DESTINATION include/system)
+ INSTALL(TARGETS ${PROJECT} DESTINATION lib)
+
+ RETURN()
+ENDIF()
+
+INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR}
+ ${NETWORK_SOURCE_DIR}/include
+ ${RESOURCED_INCLUDEDIR}
+ ${RESOURCED_SOURCE_DIR}
+ ${INCLUDE_MEMORY_DIR}
+ )
+
+SET (REQUIRES_LIST dlog
+ glib-2.0
+ sqlite3
+ vconf
+ ecore
+ edbus
+ openssl
+ tapi
+ capi-system-info
+)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs_${NETWORK} REQUIRED ${REQUIRES_LIST})
+
+FOREACH(flag ${pkgs_${NETWORK}_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC")
+
+IF("${ARCH}" STREQUAL "arm")
+ ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+#### libs ####
+
+SET(settings_HEADERS
+ ${NETWORK_SOURCE_DIR}/include/settings.h)
+
+SET(settings_SOURCES
+ ${NETWORK_SOURCE_DIR}/settings.c)
+
+ADD_LIBRARY(settings STATIC
+ ${settings_SOURCES})
+
+SET(storage_HEADERS
+ ${NETWORK_SOURCE_DIR}/include/storage.h)
+
+SET(storage_SOURCES
+ ${NETWORK_SOURCE_DIR}/datausage-quota-processing.c #storage uses it
+ ${NETWORK_SOURCE_DIR}/storage.c)
+
+SET(quota_HEADERS
+ ${NETWORK_SOURCE_DIR}/include/datausage-quota-processing.h)
+
+SET(quota_SOURCES
+ ${COMMON_SOURCE_DIR}/edbus-handler.c
+ ${NETWORK_SOURCE_DIR}/datausage-quota.c
+ )
+
+SET(app-stat_SOURCES
+ ${NETWORK_SOURCE_DIR}/app-stat.c)
+
+SET(net-iface_HEADERS
+ ${NETWORK_SOURCE_DIR}/include/iface.h)
+
+SET(net-iface_SOURCES
+ ${NETWORK_SOURCE_DIR}/iface.c
+ ${COMMON_SOURCE_DIR}/config-parser.c
+)
+
+SET(telephony_HEADERS
+ ${NETWORK_SOURCE_DIR}/include/telephony.h)
+
+SET(telephony_SOURCES
+ ${NETWORK_SOURCE_DIR}/telephony.c)
+
+ADD_LIBRARY(storage STATIC
+ ${storage_SOURCES} ${storage_HEADERS}
+ ${quota_SOURCES} ${quota_HEADERS}
+ )
+TARGET_LINK_LIBRARIES(storage ${pkgs_${NETWORK}_LDFLAGS})
+
+ADD_LIBRARY(app-stat STATIC
+ ${app-stat_SOURCES})
+TARGET_LINK_LIBRARIES(app-stat net-cls)
+
+ADD_LIBRARY(net-iface STATIC
+ ${net-iface_SOURCES})
+TARGET_LINK_LIBRARIES(net-iface resourced_shared)
+
+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)
+
+SET(net-cls_SOURCES
+ ${NETWORK_SOURCE_DIR}/net-cls-cgroup.c)
+
+ADD_LIBRARY(net-cls STATIC
+ ${net-cls_SOURCES})
+
+#### libs ####
+
+#### lib-resourced ####
+
+SET(${PROJECT}_HEADERS
+ ${INCLUDE_PUBLIC_DIR}/data_usage.h
+ ${INCLUDE_PUBLIC_DIR}/resourced.h
+ ${INCLUDE_COMMON_DIR}/const.h
+ ${INCLUDE_COMMON_DIR}/trace.h
+ ${quota_HEADERS}
+ )
+
+SET(${PROJECT}_SOURCES
+ ${COMMON_SOURCE_DIR}/trace.c
+ ${COMMON_SOURCE_DIR}/cgroup.c
+ ${COMMON_SOURCE_DIR}/appid-helper.c
+ ${COMMON_SOURCE_DIR}/file-helper.c
+ ${NETWORK_SOURCE_DIR}/foreach.c
+ ${NETWORK_SOURCE_DIR}/join.c
+ ${NETWORK_SOURCE_DIR}/options-public.c
+ ${NETWORK_SOURCE_DIR}/reset.c
+ ${NETWORK_SOURCE_DIR}/restriction.c
+ ${NETWORK_SOURCE_DIR}/update.c
+ ${NETWORK_SOURCE_DIR}/main.c
+ ${NETWORK_SOURCE_DIR}/nl-helper.c
+ ${NETWORK_SOURCE_DIR}/restriction-helper.c
+ ${NETWORK_SOURCE_DIR}/iptables-rule.c
+ ${quota_SOURCES}
+ )
+
+IF("${DATAUSAGE_TYPE}" STREQUAL "NFACCT")
+ELSE()
+ SET(${PROJECT}_SOURCES ${${PROJECT}_SOURCES}
+ ${NETWORK_SOURCE_DIR}/net-activity.c
+ ${NETWORK_SOURCE_DIR}/generic-netlink.c
+ )
+ENDIF()
+
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(shared_pkgs REQUIRED dlog
+ glib-2.0
+ sqlite3
+ vconf
+ ecore
+ edbus)
+
+FOREACH(flag ${shared_pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fPIC")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+ADD_LIBRARY(${PROJECT} SHARED
+ ${${PROJECT}_HEADERS}
+ ${${PROJECT}_SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROJECT} telephony settings net-cls
+ ${shared_pkgs_LDFLAGS})
+
+SET_TARGET_PROPERTIES(${PROJECT}
+ PROPERTIES
+ VERSION ${FULLVER}
+ SOVERSION ${MAJORVER}
+ CLEAN_DIRECT_OUTPUT 1
+)
+
+ADD_EXECUTABLE(datausagetool
+ ${UTILS_SOURCE_DIR}/datausage-tool.c
+ )
+TARGET_LINK_LIBRARIES(datausagetool
+ ${PROJECT}
+ resourced_shared
+ net-iface
+ )
+INSTALL(TARGETS datausagetool
+ DESTINATION /usr/bin)
+
+INSTALL(TARGETS ${PROJECT} DESTINATION lib)
+INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/data_usage.h DESTINATION include/system)
+#### lib-resourced ####
--- /dev/null
+/*
+ * 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 app-stat.c
+ *
+ * @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 "telephony.h"
+#include "trace.h"
+#include "notifier.h"
+#include "proc-common.h"
+#ifdef CONFIG_DATAUSAGE_NFACCT
+#include "nfacct-rule.h"
+#else
+#include "genl.h"
+#endif
+
+static void free_app(gpointer data)
+{
+ struct application_stat *app_stat = (struct application_stat *)data;
+ if (!app_stat)
+ return;
+
+ if (app_stat->application_id)
+ free(app_stat->application_id);
+
+ free(app_stat);
+}
+
+static gint compare_classid(gconstpointer a, gconstpointer b,
+ gpointer __attribute__((__unused__)) userdata)
+{
+ 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 = a_key->classid - b_key->classid;
+
+ if (ret)
+ return ret;
+
+ ret = a_key->iftype - b_key->iftype;
+ if (ret)
+ return ret;
+
+ ret = strcmp(a_key->ifname, b_key->ifname);
+ 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)
+{
+ int ret;
+ char buf[256];
+ struct application_stat_tree *app_stat_tree;
+ app_stat_tree =
+ (struct application_stat_tree *) malloc
+ (sizeof(struct application_stat_tree));
+ if (!app_stat_tree) {
+ _E("Malloc of create_app_stat_tree failed\n");
+ return NULL;
+ }
+
+ app_stat_tree->tree =
+ (GTree *)g_tree_new_full(compare_classid,
+ NULL, free,
+ free_app);
+ app_stat_tree->last_touch_time = time(0);
+ ret = pthread_rwlock_init(&app_stat_tree->guard, NULL);
+ if (ret != 0) {
+ _E("Could not initialize tree guard %s.", strerror_r(ret, buf, sizeof(buf)));
+ free(app_stat_tree);
+ app_stat_tree = NULL;
+ }
+ return app_stat_tree;
+}
+
+void free_app_stat_tree(struct application_stat_tree *app_stat_tree)
+{
+ /* do not check null pointer because it makes g_tree_destroy */
+ ret_msg_if(app_stat_tree == NULL,
+ "Please provide valid app_stat_tree!");
+ g_tree_destroy((GTree *)app_stat_tree->tree);
+}
+
+void nulify_app_stat_tree(struct application_stat_tree **app_stat_tree)
+{
+ free_app_stat_tree(*app_stat_tree);
+ free(*app_stat_tree);
+ *app_stat_tree = NULL;
+}
+
+static gboolean set_app_id(gpointer key, gpointer value,
+ void __attribute__((__unused__)) *data)
+{
+ /* Open closed principle would be better here */
+ struct application_stat *stat = (struct application_stat *)value;
+ u_int32_t classid = ((struct classid_iftype_key*)key)->classid;
+
+ /* No need to request update classid table per each app entry */
+ stat->application_id = get_app_id_by_classid(classid, false);
+ return FALSE;
+}
+
+static inline void identify_application(
+ struct application_stat_tree *app_stat_tree)
+{
+ g_tree_foreach(app_stat_tree->tree, (GTraverseFunc)set_app_id, NULL);
+}
+
+#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 *)carg->result;
+ struct application_stat *app_stat = NULL;
+ struct proc_status ps = {0};
+
+ 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, &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));
+ g_tree_insert((GTree *)app_tree->tree, (gpointer)key, (gpointer)app_stat);
+#ifdef DEBUG_ENABLED
+ _D("new app stat for classid %u\n", counter->classid);
+#endif
+ } else {
+#ifdef DEBUG_ENABLED
+ _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);
+#endif
+ }
+
+ 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_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);
+
+ ps.appid = app_stat->application_id;
+ ps.pai = find_app_info_by_appid(ps.appid);
+
+ if (ps.pai && ps.pai->state == PROC_STATE_SUSPEND) {
+ ps.pid = ps.pai->main_pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ }
+}
+
+static void fill_nfacct_restriction(struct nfacct_rule *counter, uint64_t bytes)
+{
+ /* 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;
+ }
+
+ _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) {
+ _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;
+
+ 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 */
+
+ app_stat = (struct application_stat *)
+ 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));
+ g_tree_insert((GTree *)app_tree->tree, (gpointer)key, (gpointer)app_stat);
+#ifdef DEBUG_ENABLED
+ _D("new app stat for classid %u\n", classid);
+#endif
+ }
+
+ 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);
+
+}
+#endif /* CONFIG_DATAUSAGE_NFACCT */
--- /dev/null
+/*
+ * 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 counter-process.c
+ *
+ * @desc Counter process entity
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "app-stat.h"
+#include "cgroup.h"
+#include "config.h"
+#include "const.h"
+#include "counter.h"
+#include "database.h"
+#include "datausage-common.h"
+#include "datausage-quota.h"
+#include "datausage-quota-processing.h"
+#include "datausage-restriction.h"
+#include "edbus-handler.h"
+#include "generic-netlink.h"
+#include "net-cls-cgroup.h"
+#include "nfacct-rule.h"
+#include "macro.h"
+#include "module-data.h"
+#include "notification.h"
+#include "resourced.h"
+#include "telephony.h"
+#include "storage.h"
+#include "trace.h"
+#include "transmission.h"
+#include "datausage-vconf-common.h"
+
+#include <Ecore.h>
+#include <endian.h>
+#include <glib.h>
+#include <linux/genetlink.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <stdbool.h>
+
+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, imsi, ground) " \
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
+#define REMOVE_QUOTA "DELETE FROM quotas WHERE binpath=? AND iftype=? " \
+ " AND roaming=? AND imsi=? AND ground=?"
+
+#define QUOTA_CEILING_VALUE 10737418220
+
+/* Warning threshold part in percent*/
+enum {
+ WARNING_THRESHOLD_DEFAULT_PART = 5,
+ WARNING_THRESHOLD_PART_10 = 10,
+ WARNING_THRESHOLD_PART_15 = 15,
+ WARNING_THRESHOLD_PART_20 = 20,
+};
+
+static sqlite3_stmt *datausage_quota_insert;
+static sqlite3_stmt *datausage_quota_remove;
+
+static bool check_net_blocked(sig_atomic_t state)
+{
+ static int net_blocked; /* counter for run only one time after blocking
+ to store gap value */
+ if (state & RESOURCED_NET_BLOCKED_STATE &&
+ net_blocked)
+ return true;
+
+ /* set net_blocked flag */
+ if (!net_blocked &&
+ state & RESOURCED_NET_BLOCKED_STATE)
+ ++net_blocked;
+ /* reset net_blocked flag */
+ if (net_blocked &&
+ !(state & RESOURCED_NET_BLOCKED_STATE))
+ --net_blocked;
+ _D("net_blocked %d, state %d", net_blocked, state);
+ return false;
+}
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+static Eina_Bool send_counter_request(struct counter_arg *carg)
+{
+ 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;
+}
+
+/* TODO exclude wlll be broken */
+static nfacct_rule_jump get_counter_jump(nfacct_rule_intend intend)
+{
+ if (intend == NFACCT_WARN)
+ return NFACCT_JUMP_ACCEPT;
+ else if (intend == NFACCT_BLOCK)
+ return NFACCT_JUMP_REJECT;
+
+ return NFACCT_JUMP_UNKNOWN;
+}
+
+static void populate_counters(char *cnt_name,
+ struct counter_arg *carg)
+{
+ struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
+ nfacct_rule_jump jump = NFACCT_JUMP_UNKNOWN;
+
+ if (!recreate_counter_by_name(cnt_name, &counter)) {
+ _E("Can't parse counter name %s", cnt_name);
+ return;
+ }
+
+ if (counter.intend == NFACCT_TETH_COUNTER) {
+ _D("no need to populate already created counters");
+ return;
+ }
+ counter.carg = carg;
+ strncpy(counter.name, cnt_name, sizeof(counter.name)-1);
+ jump = get_counter_jump(counter.intend);
+ _D("counter: %s, classid %u, iftype %u, iotype %d, bytes %lu", cnt_name,
+ counter.classid, counter.iftype,
+ counter.iotype);
+
+ produce_net_rule(&counter, 0, 0,
+ NFACCT_ACTION_APPEND, jump, counter.iotype);
+}
+
+static void finalize_response(const char *cnt_name, struct counter_arg *carg)
+{
+ struct nfacct_rule counter = { .carg = carg, 0 };
+
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("cnt_name %s", cnt_name);
+#endif
+ recreate_counter_by_name((char *)cnt_name, &counter);
+ finalize_counter(&counter);
+}
+
+static int fill_counters(struct rtattr *attr_list[__NFACCT_MAX],
+ void *user_data)
+{
+ struct counter_arg *carg = user_data;
+ char *cnt_name = (char *)RTA_DATA(
+ attr_list[NFACCT_NAME]);
+ if (carg->initiate)
+ populate_counters(cnt_name, carg);
+ else {
+ uint64_t *bytes_p = (uint64_t *)RTA_DATA(attr_list[NFACCT_BYTES]);
+ int bytes = be64toh(*bytes_p);
+ /* 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) {
+ ++carg->serialized_counters;
+ fill_nfacct_result(cnt_name, bytes, carg);
+ }
+ finalize_response(cnt_name, carg);
+ }
+
+ return 0;
+}
+
+static int post_fill_counters(void *user_data)
+{
+ struct counter_arg *carg = user_data;
+
+ if (carg->initiate)
+ carg->initiate = 0;
+
+ return 0;
+}
+
+#else
+static Eina_Bool send_counter_request(struct counter_arg *carg)
+{
+ int ret = send_command(carg->sock, carg->pid, carg->family_id_stat,
+ TRAF_STAT_C_GET_CONN_IN);
+ ret_value_msg_if(ret < 0, ECORE_CALLBACK_RENEW,
+ "Failed to send command to get incomming traffic");
+
+ ret = send_command(carg->sock, carg->pid, carg->family_id_stat,
+ TRAF_STAT_C_GET_PID_OUT);
+ ret_value_msg_if(ret < 0, ECORE_CALLBACK_RENEW,
+ "Failed to send command to get outgoing traffic");
+
+ return ECORE_CALLBACK_RENEW;
+}
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+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;
+ }
+
+ /* 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);
+
+ /* 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;
+
+ return cb_result;
+}
+
+static dbus_bool_t deserialize_restriction(
+ DBusMessage *msg, char **appid, resourced_net_restrictions *rest,
+ enum traffic_restriction_type *rst_type)
+{
+ DBusError err;
+ dbus_error_init(&err);
+
+ int ret = dbus_message_get_args(
+ msg, &err,
+ DBUS_TYPE_STRING, appid,
+ DBUS_TYPE_INT32, rst_type,
+ DBUS_TYPE_INT32, &(rest->rs_type),
+ DBUS_TYPE_INT32, &(rest->iftype),
+ DBUS_TYPE_INT32, &(rest->send_limit),
+ DBUS_TYPE_INT32, &(rest->rcv_limit),
+ DBUS_TYPE_INT32, &(rest->snd_warning_limit),
+ DBUS_TYPE_INT32, &(rest->rcv_warning_limit),
+ DBUS_TYPE_INT32, &(rest->roaming),
+ DBUS_TYPE_STRING, &(rest->imsi),
+ DBUS_TYPE_INVALID);
+
+ if (ret == FALSE) {
+ _E("Can't deserialize quota! [%s:%s]\n",
+ err.name, err.message);
+ }
+
+ dbus_error_free(&err);
+
+ return ret;
+}
+
+static DBusMessage *edbus_process_restriction(E_DBus_Object *obj,
+ DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ int ret;
+ resourced_ret_c dbus_ret = RESOURCED_ERROR_NONE;
+ char *appid = NULL;
+ resourced_net_restrictions rest = { 0, };
+ enum traffic_restriction_type rst_type;
+ resourced_restriction_info rst_info = {0,};
+
+ ret = dbus_message_is_method_call(
+ msg, RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_PROCESS_RESTRICTION);
+
+ if (ret == FALSE)
+ return dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD,
+ "Method is not supported");
+
+ ret = deserialize_restriction(msg, &appid, &rest, &rst_type);
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ if (ret == FALSE) {
+ dbus_ret = RESOURCED_ERROR_FAIL;
+ goto out;
+ }
+ rest.ifname = get_iftype_name(rest.iftype);
+
+ rst_info.ifname = get_iftype_name(RESOURCED_IFACE_DATACALL);
+ get_restriction_info(appid, RESOURCED_IFACE_DATACALL, &rst_info);
+ /* TODO : 2SIM device with restriction per application */
+ /* restriction is not imsi based */
+ dbus_ret = proc_keep_restriction(appid, NONE_QUOTA_ID, &rest,
+ rst_type, false, rst_info.rst_state);
+out:
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &dbus_ret);
+
+ return reply;
+}
+
+static DBusMessage *edbus_update_counters(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ struct shared_modules_data *m_data = get_shared_modules_data();
+
+ if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_UPDATE) == 0)
+ return dbus_message_new_error(msg, DBUS_ERROR_UNKNOWN_METHOD,
+ "Method is not supported");
+
+ if (m_data != NULL && m_data->carg != NULL) {
+ 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);
+ _counter_func_cb(m_data->carg);
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+}
+
+static inline int _get_threshold_part(int time_period)
+{
+ if (time_period < RESOURCED_PERIOD_DAY)
+ return WARNING_THRESHOLD_PART_20;
+
+ if (time_period < RESOURCED_PERIOD_WEEK)
+ return WARNING_THRESHOLD_PART_15;
+
+ if (time_period < RESOURCED_PERIOD_MONTH)
+ return WARNING_THRESHOLD_PART_10;
+
+ return WARNING_THRESHOLD_DEFAULT_PART;
+}
+
+static inline int64_t get_quota_ceiling(const int64_t quota)
+{
+ return quota >= QUOTA_CEILING_VALUE ? QUOTA_CEILING_VALUE :
+ quota;
+}
+
+static inline int _evaluate_warning_threshold(const int64_t quota,
+ const int time_period, const int user_threshold)
+{
+ int threshold_part = WARNING_THRESHOLD_DEFAULT_PART;
+
+ if (user_threshold != WARNING_THRESHOLD_DEFAULT)
+ return user_threshold;
+
+ threshold_part = _get_threshold_part(time_period);
+
+ return (get_quota_ceiling(quota) / 100 ) * threshold_part;
+}
+
+static dbus_bool_t deserialize_quota(
+ DBusMessage *msg, char **appid,
+ struct serialization_quota *quota)
+{
+ DBusError err;
+ dbus_error_init(&err);
+
+ int ret = dbus_message_get_args(
+ msg, &err,
+ DBUS_TYPE_STRING, appid,
+ DBUS_TYPE_INT32, "a->time_period,
+ DBUS_TYPE_UINT64, "a->snd_quota,
+ DBUS_TYPE_UINT64, "a->rcv_quota,
+ DBUS_TYPE_INT32, "a->snd_warning_threshold,
+ DBUS_TYPE_INT32, "a->rcv_warning_threshold,
+ DBUS_TYPE_INT32, "a->quota_type,
+ 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;
+ }
+
+ if (!quota->start_time) {
+ _E("Start time wasn't specified!\n");
+ ret = FALSE;
+ goto release;
+ }
+
+ 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;
+ }
+
+ _D("calculated snd_warning_threshold %d", quota->snd_warning_threshold);
+ _D("calculated rcv_warning_threshold %d", quota->rcv_warning_threshold);
+
+release:
+
+ 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,
+ char **imsi, resourced_state_t *ground)
+{
+ DBusError err;
+ dbus_error_init(&err);
+
+ int ret = dbus_message_get_args(
+ msg, &err,
+ 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",
+ err.name, err.message);
+ }
+
+ dbus_error_free(&err);
+
+ return ret;
+}
+
+static DBusMessage *edbus_join_net_stat(E_DBus_Object *obj, DBusMessage *msg)
+{
+ char *app_id = NULL;
+ int pid = 0;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError err;
+
+ dbus_error_init(&err);
+
+ if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_JOIN_NET_STAT) == 0) {
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ goto join_net_out;
+ }
+
+ ret = dbus_message_get_args(
+ msg, &err,
+ DBUS_TYPE_STRING, &app_id,
+ DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID);
+ if (ret == FALSE) {
+ _E("Can't deserialize join netstat message! [%s:%s]\n",
+ err.name, err.message);
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ goto join_net_out;
+ }
+
+ ret = join_net_cls(app_id, pid);
+
+join_net_out:
+ 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);
+ return reply;
+}
+
+static int init_datausage_quota_remove(sqlite3 *db)
+{
+ int rc;
+
+ if (datausage_quota_remove)
+ return SQLITE_OK;
+
+ rc = sqlite3_prepare_v2(db, REMOVE_QUOTA, -1,
+ &datausage_quota_remove, NULL);
+ if (rc != SQLITE_OK) {
+ _E("can not prepare datausage_quota_remove");
+ datausage_quota_remove = NULL;
+ sqlite3_finalize(datausage_quota_remove);
+ return rc;
+ }
+
+ return rc;
+}
+
+static resourced_ret_c remove_quota(const char *app_id,
+ 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();
+
+ if (init_datausage_quota_remove(resourced_get_database()) != SQLITE_OK) {
+ _D("Failed to initialize data usage quota statements: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_bind_text(datausage_quota_remove, 1, app_id, -1, SQLITE_STATIC) !=
+ SQLITE_OK) {
+ _SE("Can not bind app_id: %s for preparing statement",
+ app_id);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int(datausage_quota_remove, 2, iftype)
+ != SQLITE_OK) {
+ _E("Can not bind iftype:%d for preparing statement",
+ iftype);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int(datausage_quota_remove, 3, roaming)
+ != SQLITE_OK) {
+ _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;
+ }
+
+ if (!check_event_in_current_modem(imsi_hash, iftype))
+ check_and_clear_all_noti();
+
+ _SD("quota for app %s removed", app_id);
+
+out:
+ sqlite3_reset(datausage_quota_remove);
+ return error_code;
+}
+
+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) {
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ goto remove_out;
+ }
+
+ 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, 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);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+static int init_datausage_quota_insert(sqlite3 *db)
+{
+ int rc;
+
+ if (datausage_quota_insert)
+ return SQLITE_OK;
+
+ rc = sqlite3_prepare_v2(db, INSERT_QUERY,
+ -1, &datausage_quota_insert, NULL);
+
+ if (rc != SQLITE_OK) {
+ _E("can not prepare datausage_quota_insert");
+ datausage_quota_insert = NULL;
+ sqlite3_finalize(datausage_quota_insert);
+ }
+
+ return rc;
+}
+
+static resourced_ret_c store_quota(const char *app_id,
+ const struct serialization_quota *quota, int *quota_id)
+{
+ resourced_ret_c error_code = RESOURCED_ERROR_NONE;
+
+ libresourced_db_initialize_once();
+
+ if (init_datausage_quota_insert(resourced_get_database()) != SQLITE_OK) {
+ _D("Failed to initialize data usage quota statements: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_bind_text(datausage_quota_insert, 1, app_id, -1,
+ 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;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(datausage_quota_insert, 2,
+ quota->snd_quota) != SQLITE_OK) {
+ _E("Can not bind snd_quota: %lld for preparing statement",
+ quota->snd_quota);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(datausage_quota_insert, 3,
+ quota->rcv_quota) != SQLITE_OK) {
+ _E("Can not bind rcv_quota: %lld for preparing statement",
+ quota->rcv_quota);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(datausage_quota_insert, 4,
+ quota->snd_warning_threshold) != SQLITE_OK) {
+ _E("Can not bind snd_warning_threshold: %lld for preparing statement",
+ quota->snd_warning_threshold);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(datausage_quota_insert, 5,
+ quota->rcv_warning_threshold) != SQLITE_OK) {
+ _E("Can not bind rcv_warning_threshold: %lld for preparing statement",
+ quota->rcv_warning_threshold);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(datausage_quota_insert, 6,
+ quota->time_period) != SQLITE_OK) {
+ _E("Can not bind time_period: %d for preparing statement",
+ quota->time_period);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int(datausage_quota_insert, 7,
+ quota->start_time) != SQLITE_OK) {
+ _E("Can not bind start_time: %d for preparing statement",
+ quota->start_time);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int(datausage_quota_insert, 8,
+ quota->iftype) != SQLITE_OK) {
+ _E("Can not bind iftype: %d for preparing statement",
+ quota->iftype);
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int(datausage_quota_insert, 9,
+ quota->roaming_type) != SQLITE_OK) {
+ _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;
+ }
+
+ if (sqlite3_step(datausage_quota_insert) != SQLITE_DONE) {
+ _E("Failed to record quota %s.",
+ sqlite3_errmsg(resourced_get_database()));
+ 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;
+}
+
+static DBusMessage *edbus_create_quota(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ 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;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+
+ 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 update_out;
+ }
+
+ carg = m_data->carg;
+
+ if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_CREATE_QUOTA) == 0) {
+ _E("Invalid DBUS argument");
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ goto update_out;
+ }
+
+ 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_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!");
+
+ SET_BIT(carg->opts->state, RESOURCED_CHECK_QUOTA);
+ reschedule_count_timer(carg, 0);
+#ifdef DEBUG_ENABLED
+ _SD("Datausage quota changed");
+#endif
+
+update_out:
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+struct get_stats_context {
+ DBusMessage *reply;
+ DBusMessage *msg;
+ int info_count;
+ GSList *infos;
+ DBusMessageIter iter;
+};
+
+static resourced_cb_ret answer_get_stat(const data_usage_info *info,
+ void *user_data)
+{
+ struct get_stats_context *ctx = (struct get_stats_context *)user_data;
+ data_usage_info *insert = (data_usage_info *)malloc(sizeof(data_usage_info));
+
+ ret_value_msg_if(insert == NULL, RESOURCED_CANCEL, "Can't allocate memory!");
+ memcpy(insert, info, sizeof(data_usage_info));
+ if (info->app_id) {
+ int app_id_len = strlen(info->app_id) + 1;
+ insert->app_id = (char *)malloc(app_id_len);
+ if (!insert->app_id) {
+ free(insert);
+ _E("Malloc of answer_get_stat failed\n");
+ return RESOURCED_CANCEL;
+ }
+
+ strncpy((char *)insert->app_id, info->app_id, app_id_len);
+ }
+ ctx->infos = g_slist_append(ctx->infos, insert);
+ return RESOURCED_CONTINUE;
+}
+
+static void prepare_response(struct get_stats_context *ctx)
+{
+ GSList *iter;
+ data_usage_info *info;
+ DBusMessageIter arr;
+
+ ctx->reply = dbus_message_new_method_return(ctx->msg);
+ dbus_message_iter_init_append(ctx->reply, &ctx->iter);
+ dbus_message_iter_open_container(&ctx->iter, DBUS_TYPE_ARRAY, "(siiiiiii)", &arr);
+
+ gslist_for_each_item(iter, ctx->infos) {
+ info = (data_usage_info *)iter->data;
+
+ DBusMessageIter sub;
+
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ if (info->app_id == NULL)
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
+ &null_str);
+ else
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
+ &info->app_id);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &info->iftype);
+ 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->cnt.incoming_bytes);
+ /* 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);
+
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+
+ dbus_message_iter_close_container(&ctx->iter, &arr);
+ g_slist_free_full(ctx->infos, free);
+}
+
+static void deserialize_rule(DBusMessage *msg, data_usage_selection_rule *rule, char **app_id)
+{
+ DBusError err;
+ dbus_error_init(&err);
+
+ int ret = dbus_message_get_args(
+ msg, &err,
+ DBUS_TYPE_STRING, app_id,
+ DBUS_TYPE_INT32, &rule->from,
+ DBUS_TYPE_INT32, &rule->to,
+ DBUS_TYPE_INT32, &rule->iftype,
+ DBUS_TYPE_INT32, &rule->granularity,
+ DBUS_TYPE_INVALID);
+
+ if (ret == FALSE) {
+ _E("Can't deserialize quota! [%s:%s]\n",
+ err.name, err.message);
+ }
+
+ if (app_id && !strcmp(*app_id, null_str))
+ *app_id = NULL;
+ dbus_error_free(&err);
+}
+
+static DBusMessage *edbus_get_stats(E_DBus_Object *obj, DBusMessage *msg)
+{
+ data_usage_selection_rule rule;
+ char *app_id = NULL;
+ resourced_ret_c ret;
+ struct get_stats_context ctx;
+ ctx.infos = NULL;
+
+ if (dbus_message_is_method_call(msg, RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_GET_STATS) == 0) {
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ goto update_out;
+ }
+#ifdef DEBUG_ENABLED
+ _SD("Datausage get stats");
+#endif
+ ctx.msg = msg;
+ deserialize_rule(msg, &rule, &app_id);
+ if (app_id)
+ ret = data_usage_details_foreach(app_id, &rule, answer_get_stat,
+ &ctx);
+ else
+ ret = data_usage_foreach(&rule, answer_get_stat, &ctx);
+
+ prepare_response(&ctx);
+ return ctx.reply;
+
+update_out:
+ ctx.reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(ctx.reply, &ctx.iter);
+ dbus_message_iter_append_basic(&ctx.iter, DBUS_TYPE_INT32, &ret);
+ return ctx.reply;
+}
+
+struct nl_family_params {
+ struct genl *ans;
+ struct counter_arg *carg;
+};
+
+typedef struct {
+ struct nl_family_params params;
+ void (*process)(struct nl_family_params *params);
+} nl_serialization_command;
+
+static inline char *get_public_appid(const uint32_t classid)
+{
+ char *appid;
+
+ /* 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 strdup(RESOURCED_ALL_APP);
+
+ appid = get_app_id_by_classid(classid, true);
+ return !appid ? strdup(UNKNOWN_APP) : appid;
+}
+
+static bool need_flush_immediatelly(sig_atomic_t state)
+{
+ return CHECK_BIT(state, RESOURCED_FORCIBLY_FLUSH_STATE) ||
+ CHECK_BIT(state, RESOURCED_FORCIBLY_QUIT_STATE);
+}
+
+static void free_restriction_info(void *data)
+{
+ resourced_restriction_info *info = (resourced_restriction_info *)data;
+ if (!info)
+ return;
+ if (info->app_id)
+ free((char *)info->app_id);
+ if (info->ifname)
+ free((char *)info->ifname);
+ if (info->imsi)
+ free((char *)info->imsi);
+}
+
+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, GLOBAL_CONFIG_IMSI);
+ }
+
+ 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 (check_flush_time(arg->opts->flush_period, arg->last_run_time) &&
+ !need_flush_immediatelly(arg->opts->state))
+ goto quit_counter;
+
+ /* 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 (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,
+ RESOURCED_NETWORK_UPDATE_FINISH,
+ DBUS_TYPE_INVALID, NULL))
+ _E("Failed to send DBUS message\n");
+ }
+ }
+
+ arg->serialized_counters = 0;
+ time(&(arg->last_run_time));
+
+quit_counter:
+ arg->store_result_timer = NULL;
+
+ /*
+ * 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)
+{
+ 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(0,
+ store_and_free_result_cb, arg);
+}
+
+static void _process_network_counter(struct nl_family_params *params)
+{
+ resourced_ret_c ret;
+ struct netlink_serialization_params ser_params = {
+ .carg = params->carg,
+ .ans = params->ans,
+#ifdef CONFIG_DATAUSAGE_NFACCT
+ .eval_attr = fill_counters,
+ .post_eval_attr = post_fill_counters,
+#endif
+ };
+
+ netlink_serialization_command *netlink =
+ netlink_create_command(&ser_params);
+
+ if (!netlink) {
+ _E("Can not create command");
+ return;
+ }
+
+ netlink->deserialize_answer(&(netlink->params));
+
+
+ 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 */
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("There is no serialized counters in response");
+#endif
+ return;
+ }
+
+ ret = process_quota(params->carg);
+ if (ret != 0) {
+ _E("Failed to process quota!");
+ return;
+ }
+
+ store_and_free_result(params->carg);
+}
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+static resourced_ret_c choose_netlink_process(struct genl *ans, nl_serialization_command *command,
+ struct counter_arg *carg)
+{
+ command->process = _process_network_counter;
+ return RESOURCED_ERROR_NONE;
+}
+#else
+
+static void _process_restriction(struct nl_family_params *cmd)
+{
+ struct traffic_restriction restriction = {0,};
+ uint8_t notification_type = RESTRICTION_NOTI_C_UNSPEC;
+ 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");
+
+ if (process_netlink_restriction_msg(cmd->ans, &restriction,
+ ¬ification_type) !=
+ RESOURCED_ERROR_NONE) {
+ _E("Failed to process netlink restriction.");
+ return;
+ }
+
+ app_id = get_public_appid(restriction.sk_classid);
+ if (!app_id) {
+ _E("Failed to get_public_appid.");
+ return;
+ }
+
+ iftype = get_iftype(restriction.ifindex);
+
+ ret = get_restriction_info(app_id, iftype, &rst_info);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to get restriction info!");
+ free(app_id);
+ return;
+ }
+
+ 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, &du_quota);
+ update_restriction_db(app_id, iftype, 0, 0,
+ RESOURCED_RESTRICTION_ACTIVATED,
+ rst_info.quota_id, rst_info.roaming, rst_info.ifname, GLOBAL_CONFIG_IMSI);
+ } 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, &du_quota);
+ } else
+ _E("Unkown restriction notification type");
+ free(app_id);
+}
+
+static resourced_ret_c choose_netlink_process(struct genl *ans,
+ nl_serialization_command *command, struct counter_arg *carg)
+{
+ int family = netlink_get_family(ans);
+
+ if (family == carg->family_id_restriction)
+ command->process = _process_restriction;
+ else if (family == carg->family_id_stat)
+ command->process = _process_network_counter;
+ else {
+ _E("General netlink family %d unsupported!", family);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+static nl_serialization_command *choose_handler(struct genl *ans,
+ struct counter_arg *carg)
+{
+ static nl_serialization_command command;
+ resourced_ret_c ret;
+
+ if (!ans || !carg) {
+ _E("Please provide valid pointer!");
+ return NULL;
+ }
+
+ if (!command.params.carg)
+ command.params.carg = carg;
+ command.params.ans = ans;
+
+ ret = choose_netlink_process(ans, &command, carg);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, NULL,
+ "Could not choose proper netlink process function! \n");
+
+ return &command;
+}
+
+static Eina_Bool _answer_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ struct counter_arg *carg = (struct counter_arg *)user_data;
+ struct genl ans;
+ nl_serialization_command *netlink_handler = NULL;
+ int ret;
+
+ ret = read_netlink(carg->sock, &ans, sizeof(struct genl));
+ if (ret == 0)
+ goto out;
+ carg->ans_len = ret;
+ netlink_handler = choose_handler(&ans, carg);
+
+ if (!netlink_handler)
+ goto out;
+
+ netlink_handler->process(&(netlink_handler->params));
+
+out:
+ return ECORE_CALLBACK_RENEW;
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { RESOURCED_NETWORK_UPDATE, NULL, NULL, edbus_update_counters },
+ { RESOURCED_NETWORK_PROCESS_RESTRICTION, NULL, NULL,
+ edbus_process_restriction },
+ { RESOURCED_NETWORK_CREATE_QUOTA, NULL, NULL, edbus_create_quota },
+ { RESOURCED_NETWORK_REMOVE_QUOTA, NULL, NULL, edbus_remove_quota },
+ { RESOURCED_NETWORK_JOIN_NET_STAT, NULL, NULL, edbus_join_net_stat },
+ { RESOURCED_NETWORK_GET_STATS, "siiii", "a(siiiiiii)", edbus_get_stats },
+};
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+int init_sock(struct counter_arg *carg)
+{
+ carg->sock = create_netlink(NETLINK_NETFILTER, 0);
+ return carg->sock != 0 ? RESOURCED_ERROR_NONE :
+ RESOURCED_ERROR_FAIL;
+}
+#else
+int init_sock(struct counter_arg *carg)
+{
+ int error = RESOURCED_ERROR_NONE;
+ carg->sock = create_netlink(NETLINK_GENERIC, 0);
+
+ ret_value_msg_if(carg->sock < 0, RESOURCED_ERROR_FAIL,
+ "Failed to create and bind netlink socket.");
+
+ carg->family_id_stat = get_family_id(carg->sock,
+ carg->pid, "TRAF_STAT");
+ if (carg->family_id_stat == 0) {
+ _E("Failed to get family id for TRAF_STAT.");
+ error = RESOURCED_ERROR_FAIL;
+ goto release_sock;
+ }
+
+ carg->family_id_restriction = get_family_id(carg->sock,
+ carg->pid, "REST_NOTI");
+
+ if (carg->family_id_restriction == 0) {
+ _E("Failed to get family id for REST_NOTI.");
+ error = RESOURCED_ERROR_FAIL;
+ goto release_sock;
+ }
+ /*thereafter we'll be able to receive message from server */
+ send_start(carg->sock, carg->pid, carg->family_id_stat);
+
+ return RESOURCED_ERROR_NONE;
+release_sock:
+ close(carg->sock);
+ return error;
+}
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+int resourced_init_counter_func(struct counter_arg *carg)
+{
+ int error = 0;
+
+ if (!carg) {
+ _E("Please provide valid argument for counting routine.");
+ error = RESOURCED_ERROR_INVALID_PARAMETER;
+ return error;
+ }
+
+ error = init_sock(carg);
+ ret_value_msg_if(error != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL,
+ "Couldn't init socket!");
+
+ carg->result = create_app_stat_tree();
+#ifdef CONFIG_DATAUSAGE_NFACCT
+ carg->nf_cntrs = create_nfacct_tree();
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+ init_iftype();
+
+ error = edbus_add_methods(RESOURCED_PATH_NETWORK, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ if (error != RESOURCED_ERROR_NONE)
+ _E("DBus method registration for %s is failed",
+ RESOURCED_PATH_NETWORK);
+
+ _counter_func_cb(carg);
+
+ carg->ecore_timer = ecore_timer_add(carg->opts->update_period,
+ _counter_func_cb, carg);
+
+ ret_value_msg_if(carg->ecore_timer == 0, RESOURCED_ERROR_FAIL,
+ "carg_timer is null, can't work! update period: %d",
+ carg->opts->update_period);
+
+ carg->ecore_fd_handler = ecore_main_fd_handler_add(
+ carg->sock, ECORE_FD_READ, _answer_func_cb, carg, NULL, NULL);
+ _D("ecore_carg_handler = %p", carg->ecore_fd_handler);
+
+ return error;
+}
+
+static void finalize_quota_insert(void)
+{
+ if (datausage_quota_insert) {
+ sqlite3_finalize(datausage_quota_insert);
+ datausage_quota_insert = NULL;
+ }
+}
+
+static void finalize_quota_remove(void)
+{
+ if (datausage_quota_remove) {
+ sqlite3_finalize(datausage_quota_remove);
+ datausage_quota_remove = NULL;
+ }
+}
+
+void resourced_finalize_counter_func(struct counter_arg *carg)
+{
+ ret_msg_if(carg == NULL, "Invalid counter argument\n");
+ 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();
+}
--- /dev/null
+/*
+ * 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: counter.c
+ * @desc Entity for working with datausage counter.
+ *
+ */
+
+#include "app-stat.h"
+#include "counter.h"
+#include "macro.h"
+#include "trace.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+struct counter_arg *init_counter_arg(struct net_counter_opts *opts)
+{
+ struct counter_arg *result =
+ (struct counter_arg *)calloc(1, sizeof(struct counter_arg));
+
+ ret_value_msg_if(result == NULL, NULL, "Not enough memory\n");
+#ifndef CONFIG_DATAUSAGE_NFACCT
+ result->pid = getpid();
+#endif
+ result->opts = opts;
+ result->serialized_counters = 0;
+ return result;
+}
+
+void finalize_carg(struct counter_arg *carg)
+{
+ free(carg);
+}
+
+void reschedule_count_timer(const struct counter_arg *carg, const double delay)
+{
+ ret_msg_if(!carg || !carg->ecore_timer,
+ "Invalid counter argument or carg_timer is null\n");
+ ecore_timer_delay(carg->ecore_timer,
+ delay - ecore_timer_pending_get(carg->ecore_timer));
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2014 - 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 datausage.c
+ *
+ * @desc Datausage module
+ *
+ */
+
+#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"
+#include "notifier.h"
+#include "notification.h" /* for sending datausage dbus notification */
+#include "datausage-common.h"
+#include "datausage-quota.h"
+#include "datausage-quota-processing.h"
+#include "datausage-vconf-callbacks.h"
+#include "iface-cb.h"
+#include "iptables-rule.h"
+#include "macro.h"
+#include "module-data.h"
+#include "module.h"
+#include "nfacct-rule.h"
+#include "resourced.h"
+#include "restriction-handler.h"
+#include "telephony.h"
+#include "tethering-restriction.h"
+#include "storage.h"
+#include "trace.h"
+#include "proc-common.h"
+
+#include <linux/rtnetlink.h>
+#include <glib.h>
+#include <inttypes.h>
+#include <Ecore.h>
+
+/* time in seconds, 2 minutes */
+#define DELAY_DELETE_INVERVAL (60*2)
+#define DELAY_STATE_INVERVAL (3)
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+#define RESTRICTION_EXCLUDE(x) ((x) == RESOURCED_RESTRICTION_EXCLUDED)
+static bool display_off = false;
+
+struct make_rule_context {
+ struct counter_arg *carg;
+ struct nfacct_rule *counter;
+};
+
+struct nfacct_key {
+ u_int32_t classid;
+ resourced_iface_type iftype;
+ nfacct_rule_direction iotype;
+ char ifname[MAX_IFACE_LENGTH];
+ nfacct_rule_intend intend;
+};
+
+enum nfacct_state {
+ NFACCT_STATE_ACTIVE, /* kernel counter is applied */
+ 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 iptc_holder {
+ struct ipt_context *iptc;
+ size_t count_down;
+};
+
+struct nfacct_value {
+ pid_t pid;
+ 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;
+ char *imsi;
+ /* end restriction part */
+ resourced_state_t ground; /* background/foreground state */
+};
+
+struct ground_state_info{
+ struct counter_arg *carg;
+ u_int32_t classid;
+ resourced_state_t state;
+};
+
+static struct nfacct_value *lookup_counter(struct nfacct_rule *counter);
+static void handle_change_allowance(resourced_iface_type iftype, bool enabled);
+static gboolean deactivate_each_counter_by_iftype(gpointer key, gpointer value, gpointer data);
+
+static nfacct_rule_jump get_jump_by_intend(struct nfacct_rule *counter)
+{
+ if (counter->intend == NFACCT_WARN)
+ return NFACCT_JUMP_ACCEPT;
+ else if (counter->intend == NFACCT_BLOCK)
+ return NFACCT_JUMP_REJECT;
+
+ return NFACCT_JUMP_UNKNOWN;
+}
+
+static resourced_ret_c add_iptables_in(struct nfacct_rule *counter)
+{
+ return produce_net_rule(counter, 0, 0,
+ NFACCT_ACTION_INSERT, get_jump_by_intend(counter),
+ NFACCT_COUNTER_IN);
+}
+
+static resourced_ret_c add_iptables_out(struct nfacct_rule *counter)
+{
+ return produce_net_rule(counter, 0, 0,
+ NFACCT_ACTION_INSERT, get_jump_by_intend(counter),
+ NFACCT_COUNTER_OUT);
+}
+
+static resourced_ret_c del_iptables_in(struct nfacct_rule *counter)
+{
+ return produce_net_rule(counter, 0, 0,
+ NFACCT_ACTION_DELETE, get_jump_by_intend(counter),
+ NFACCT_COUNTER_IN);
+}
+
+static resourced_ret_c del_iptables_out(struct nfacct_rule *counter)
+{
+ return produce_net_rule(counter, 0, 0,
+ NFACCT_ACTION_DELETE, get_jump_by_intend(counter),
+ NFACCT_COUNTER_OUT);
+}
+
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+static gboolean bg_restriction(gpointer key, gpointer value,
+ gpointer data)
+{
+ bool *bg_restriction = (bool *)data;
+
+ struct nfacct_key *nf_key = (struct nfacct_key *)key;
+ struct nfacct_value *nf_value = (struct nfacct_value *)value;
+
+ if (nf_value->rst_state == RESOURCED_RESTRICTION_ACTIVATED &&
+ nf_key->classid == RESOURCED_BACKGROUND_APP_CLASSID) {
+ *bg_restriction = true;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool check_bg_restriction_applied(struct counter_arg *carg)
+{
+ bool restriction = false;
+ g_tree_foreach(carg->nf_cntrs, bg_restriction, &restriction);
+ return restriction;
+}
+
+static bool check_background_applied(void)
+{
+ struct shared_modules_data *m_data;
+ struct counter_arg *carg;
+
+ 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!");
+
+ return get_background_quota() || check_bg_restriction_applied(carg);
+}
+
+
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+
+static int app_launch_srv_cb(void *data)
+{
+ struct proc_status *ps = (struct proc_status*)data;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ bool background_quota;
+ struct proc_app_info *pai;
+ struct proc_program_info *ppi;
+ resourced_restriction_info rst_info = {0};
+
+ ret_value_msg_if(ps == NULL, RESOURCED_ERROR_FAIL,
+ "Please provide valid argument!");
+ ret = join_net_cls(ps->appid, ps->pid);
+ if (ret != RESOURCED_ERROR_NONE)
+ _D("Failed to start network counting.");
+
+ mark_background(ps->appid);
+
+ rst_info.ifname = get_iftype_name(RESOURCED_IFACE_DATACALL);
+ get_restriction_info(ps->appid, RESOURCED_IFACE_DATACALL, &rst_info);
+ background_quota = check_background_applied();
+
+ if (background_quota && !RESTRICTION_EXCLUDE(rst_info.rst_state))
+ ret = make_net_cls_cgroup_with_pid(ps->pid, RESOURCED_BACKGROUND_APP_NAME);
+
+ if (ps->pai && ps->pai->program) {
+ ppi = ps->pai->program;
+ if (ppi->app_list) {
+ pai = (struct proc_app_info *)g_slist_nth_data(ppi->app_list, 0);
+ place_pids_to_net_cgroup(ps->pid, pai->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)key;
+ char *ifname = (char *)value;
+ struct nfacct_value *nf_value;
+
+ if (iftype == RESOURCED_IFACE_UNKNOWN)
+ return FALSE;
+
+ counter->iftype = iftype;
+ STRING_SAVE_COPY(counter->ifname, ifname);
+
+ nf_value = lookup_counter(counter);
+
+ 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;
+}
+
+static void remove_nfacct_counters_for_all_iface(u_int32_t classid, struct counter_arg *carg)
+{
+ struct nfacct_rule counter = {
+ .classid = classid,
+ .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 */
+ };
+
+ /* remove for ingress 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_ifnames((ifnames_iterator)remove_each_counter, NULL, &counter);
+}
+
+struct match_nftree_context
+{
+ u_int32_t classid;
+ pid_t pid;
+};
+
+static gboolean match_pid(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ struct match_nftree_context *ctx = (struct match_nftree_context *)data;
+ struct nfacct_value *nf_value = (struct nfacct_value *)value;
+ struct nfacct_key *nf_key = (struct nfacct_key *)key;
+ if (nf_value->pid == ctx->pid) {
+ ctx->classid = nf_key->classid;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static u_int32_t get_classid_by_pid(struct counter_arg *carg, const pid_t pid)
+{
+ struct match_nftree_context ctx = {
+ .pid = pid,
+ .classid = RESOURCED_UNKNOWN_CLASSID,
+ };
+ g_tree_foreach(carg->nf_cntrs, match_pid, &ctx);
+ 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) {
+ strncpy(rule.ifname, nf_key->ifname, sizeof(rule.ifname)-1);
+ rule.ifname[sizeof(rule.ifname)-1] = 0;
+ 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);
+}
+
+void move_pids_tree_to_cgroup(struct proc_app_info *pai,
+ const char *pkg_name)
+{
+ GSList *iter = NULL;
+ struct proc_app_info *ai = NULL;
+ if (pai->childs) {
+ gslist_for_each_item(iter, pai->childs) {
+ struct child_pid *child = (struct child_pid *)(iter->data);
+ place_pids_to_net_cgroup(child->pid, pkg_name);
+ }
+ }
+ if (pai->program && pai->program->svc_list) {
+ gslist_for_each_item(iter, pai->program->svc_list) {
+ ai = (struct proc_app_info *)(iter->data);
+ place_pids_to_net_cgroup(ai->main_pid, pkg_name);
+ }
+ }
+}
+
+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;
+ struct proc_app_info *pai = NULL;
+ resourced_restriction_info rst_info = {0};
+ char *app_id;
+
+ if (nf_value->ground != RESOURCED_STATE_BACKGROUND ||
+ nf_key->classid == RESOURCED_ALL_APP_CLASSID ||
+ nf_key->intend != NFACCT_COUNTER)
+ return FALSE;
+
+ app_id = get_app_id_by_classid(nf_key->classid, false);
+
+ if (!app_id)
+ return FALSE;
+
+ rst_info.ifname = get_iftype_name(RESOURCED_IFACE_DATACALL);
+ get_restriction_info(app_id, RESOURCED_IFACE_DATACALL, &rst_info);
+
+ /* move into background cgroup */
+ if (state == RESOURCED_STATE_BACKGROUND &&
+ !RESTRICTION_EXCLUDE(rst_info.rst_state)) {
+ make_net_cls_cgroup_with_pid(nf_value->pid, RESOURCED_BACKGROUND_APP_NAME);
+ pai = find_app_info(nf_value->pid);
+ if (pai)
+ move_pids_tree_to_cgroup(pai, RESOURCED_BACKGROUND_APP_NAME);
+ } else {
+ make_net_cls_cgroup_with_pid(nf_value->pid, app_id);
+ pai = find_app_info(nf_value->pid);
+ if (pai)
+ move_pids_tree_to_cgroup(pai, app_id);
+ }
+
+ free(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 resourced_app_wakeup(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ place_pids_to_net_cgroup(p_data->pid, p_data->appid);
+
+ return 0;
+}
+
+static resourced_ret_c send_counter_request(struct counter_arg *carg)
+{
+ 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;
+}
+
+static Eina_Bool update_ground_state(void *user_data)
+{
+ struct ground_state_info *arg = (struct ground_state_info *)user_data;
+
+ mark_ground_state(arg->carg, arg->classid, arg->state);
+ SET_BIT(arg->carg->opts->state, RESOURCED_DEFAULT_STATE);
+ free(arg);
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static int app_launch_cb(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+
+ 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.");
+ return ret;
+}
+
+static int resourced_app_foregrd(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ struct shared_modules_data *m_data;
+ struct counter_arg *carg;
+ struct ground_state_info *info;
+ u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
+
+ 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!");
+
+ ret = join_net_cls(p_data->appid, p_data->pid);
+ if (ret != RESOURCED_ERROR_NONE)
+ _D("Failed to start network counting.");
+ move_pids_tree_to_cgroup(p_data->pai, p_data->appid);
+
+ /* find in tree nf_cntr and mark it as foreground */
+ classid = get_classid_by_app_id(p_data->appid, false);
+
+ /* send request for the counter */
+ ret = send_counter_request(carg);
+ if (ret != RESOURCED_ERROR_NONE)
+ _D("Failed to send_counter_request.");
+
+ info = (struct ground_state_info *) malloc(sizeof(struct ground_state_info));
+ ret_value_msg_if(info == NULL, RESOURCED_ERROR_OUT_OF_MEMORY,
+ "Out of Memory !");
+
+ info->carg = carg;
+ info->classid = classid;
+ info->state = RESOURCED_STATE_FOREGROUND;
+
+ SET_BIT(carg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE);
+ ecore_timer_add(DELAY_STATE_INVERVAL, update_ground_state, info);
+
+ return ret;
+}
+
+#ifdef RESOURCED_RESUME
+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);
+
+ /* find in tree nf_cntr and mark it as background */
+
+ nfacct_number = mark_ground_state(carg, classid,
+ RESOURCED_STATE_FOREGROUND);
+ background_quota = check_background_applied();
+ if (!nfacct_number || background_quota) {
+ 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) {
+ make_net_cls_cgroup_with_pid(p_data->pid, p_data->appid);
+ if (p_data->pai->childs) {
+ GSList *iter;
+ gslist_for_each_item(iter, p_data->pai->childs) {
+ struct child_pid *child = (struct child_pid *)(iter->data);
+ place_pids_to_net_cgroup(child->pid, p_data->appid);
+ }
+ }
+ if (p_data->pai->program->svc_list) {
+ GSList *iter;
+ struct proc_app_info *pai;
+ gslist_for_each_item(iter, p_data->pai->program->svc_list) {
+ pai = (struct proc_app_info *)(iter->data);
+ place_pids_to_net_cgroup(pai->main_pid, pai->appid);
+ }
+ }
+ /*
+ * join_net_cls will create the cgroup and
+ * apply iptable rule.
+ * when the application moves to background
+ * the cgroup is deleted , so when it resumes
+ * we need to create the cgroup and also apply
+ * iptable rule.
+ */
+ move_pids_tree_to_cgroup(p_data->pai, p_data->appid);
+ }
+ return ret;
+}
+#endif
+
+static int app_terminated_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;
+ GSList *iter;
+ struct proc_app_info *ai;
+ struct proc_app_info *pai;
+ bool background_quota;
+ resourced_restriction_info rst_info = {0};
+
+ 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!");
+
+ pai = p_data->pai;
+ rst_info.ifname = get_iftype_name(RESOURCED_IFACE_DATACALL);
+ get_restriction_info(p_data->appid, RESOURCED_IFACE_DATACALL, &rst_info);
+ background_quota = check_background_applied();
+
+ if (pai && pai->program && pai->program->svc_list) {
+ gslist_for_each_item(iter, pai->program->svc_list) {
+ ai = (struct proc_app_info *)(iter->data);
+ if (background_quota && !RESTRICTION_EXCLUDE(rst_info.rst_state))
+ make_net_cls_cgroup_with_pid(ai->main_pid,
+ RESOURCED_BACKGROUND_APP_NAME);
+ }
+ } else {
+ classid = get_classid_by_pid(carg, p_data->pid);
+ ret_value_msg_if(classid == RESOURCED_UNKNOWN_CLASSID,
+ RESOURCED_ERROR_FAIL, "No classid to terminate!");
+ remove_nfacct_counters_for_all_iface(classid, carg);
+ }
+
+ 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;
+ struct ground_state_info *info;
+ resourced_restriction_info rst_info = {0};
+ bool background_quota = false;
+ resourced_ret_c ret;
+ 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_app_id(p_data->appid, false);
+
+ 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 */
+ /* send request for the counter */
+ ret = send_counter_request(carg);
+ if (ret != RESOURCED_ERROR_NONE)
+ _D("Failed to send_counter_request.");
+
+ info = (struct ground_state_info *) malloc(sizeof(struct ground_state_info));
+ ret_value_msg_if(info == NULL, RESOURCED_ERROR_OUT_OF_MEMORY,
+ "Out of Memory !");
+
+ info->carg = carg;
+ info->classid = classid;
+ info->state = RESOURCED_STATE_BACKGROUND;
+
+ SET_BIT(carg->opts->state, RESOURCED_FORCIBLY_FLUSH_STATE);
+ ecore_timer_add(DELAY_STATE_INVERVAL, update_ground_state, info);
+
+ rst_info.ifname = get_iftype_name(RESOURCED_IFACE_DATACALL);
+ get_restriction_info(p_data->appid, RESOURCED_IFACE_DATACALL, &rst_info);
+
+ if (!RESTRICTION_EXCLUDE(rst_info.rst_state)) {
+ background_quota = check_background_applied();
+ /**
+ * if we have applied background quota, put current pid into
+ * background cgroup
+ */
+ if (background_quota) {
+ make_net_cls_cgroup_with_pid(p_data->pid, RESOURCED_BACKGROUND_APP_NAME);
+ move_pids_tree_to_cgroup(p_data->pai, RESOURCED_BACKGROUND_APP_NAME);
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+
+static gboolean populate_classid_tree(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ GTree *classid_tree = (GTree *)data;
+ struct nfacct_key *nf_key = (struct nfacct_key *)key;
+ struct nfacct_value *nf_value = (struct nfacct_value *)value;
+
+ if (nf_value->state == NFACCT_STATE_ACTIVE)
+ g_tree_insert(classid_tree, (const gpointer)nf_key->classid, NULL);
+ return FALSE;
+}
+
+static gboolean remove_each_counter_by_classid(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ u_int32_t classid = (u_int32_t)key;
+ struct counter_arg *carg = (struct counter_arg *)data;
+ remove_nfacct_counters_for_all_iface(classid, carg);
+ return FALSE;
+}
+
+static gint pointer_compare(gconstpointer a, gconstpointer b)
+{
+ return a - b;
+}
+
+static int add_one_tizen_os_counter(
+ gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ struct counter_arg *carg = (struct counter_arg *)data;
+ struct nfacct_rule counter = {.name = {0}, .ifname = {0}, 0};
+ resourced_iface_type iftype = (resourced_iface_type)key;
+ char *ifname = (char *)value;
+
+ if (iftype <= RESOURCED_IFACE_UNKNOWN ||
+ iftype >= RESOURCED_IFACE_LAST_ELEM)
+ return FALSE;
+
+ counter.iotype = NFACCT_COUNTER_IN;
+ counter.iftype = iftype;
+ counter.carg = carg;
+ STRING_SAVE_COPY(counter.ifname, ifname);
+ generate_counter_name(&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);
+ 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_ifnames((ifnames_iterator)add_one_tizen_os_counter, NULL, carg);
+}
+
+static void reload_all_nf_counters(struct counter_arg *carg)
+{
+ add_tizen_os_counters(carg);
+ /**
+ * it can be done by following ways:
+ * 1. just by reading existing net_cls cgroups, looks not robust because
+ * in this case we are getting network interface type from runtime, and
+ * it could be changed since the resourced was stopped. And it doesn't
+ * reflect counter state
+ * 2. By reading from iptables rules. We don't have C code for retriving
+ * it from kernel unless to use iptables cmd output, but it's not
+ * robust and not performance effective
+ * 3. Just by obtaining nfacct counters. We could do it without command
+ * line tool. It reflects current counter state, but not,
+ * iptables rules
+ */
+ carg->initiate = 1;
+ nfacct_send_initiate(carg);
+}
+
+static void remove_whole_nf_counters(struct counter_arg *carg)
+{
+ GTree *classid_tree = g_tree_new(pointer_compare);; /* tree instead of array for avoiding
+ duplication, manual sort and binary search in case of array */
+ ret_msg_if(carg == NULL,
+ "Cant' get counter arg!");
+
+ /**
+ * fill classid list, due we couldn't iterate on tree and
+ * remove elements from it
+ */
+ g_tree_foreach(carg->nf_cntrs, populate_classid_tree, classid_tree);
+ g_tree_foreach(classid_tree, remove_each_counter_by_classid, carg);
+
+ g_tree_destroy(carg->nf_cntrs);
+ g_tree_destroy(classid_tree);
+}
+
+/**
+ * TODO use following constant from kernel header
+ * nfacct/include/linux/netfilter/nfnetlink.h
+ */
+#ifndef NFNLGRP_ACCT_QUOTA
+#define NFNLGRP_ACCT_QUOTA 8
+#endif
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+static inline char *get_public_appid(const uint32_t classid)
+{
+ char *appid;
+
+ /**
+ * 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 strndup(RESOURCED_ALL_APP, strlen(RESOURCED_ALL_APP));
+ if (classid == RESOURCED_BACKGROUND_APP_CLASSID)
+ return strndup(RESOURCED_BACKGROUND_APP_NAME, strlen(RESOURCED_BACKGROUND_APP_NAME));
+
+ appid = get_app_id_by_classid(classid, true);
+ return !appid ? strndup(UNKNOWN_APP, strlen(UNKNOWN_APP)) : appid;
+}
+
+static void init_nfacct(u_int32_t classid, pid_t pid,
+ nfacct_rule_direction ctype, struct counter_arg *carg,
+ struct nfacct_rule *counter)
+{
+ counter->iotype = ctype;
+ counter->classid = classid;
+ counter->carg = carg;
+ 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)
+ counter->iptables_rule = add_iptables_out;
+}
+
+static resourced_ret_c del_counter(struct nfacct_rule *counter)
+{
+ return produce_net_rule(counter, 0, 0,
+ NFACCT_ACTION_DELETE, get_jump_by_intend(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;
+ }
+#ifdef DEBUG_ENABLED
+ _D("quota rcv: % " PRId64 ", send: % " PRId64 " ", du_quota->rcv_quota,
+ du_quota->snd_quota);
+ _D("quota roaming: %d", du_quota->roaming_type);
+#endif
+}
+
+static int fill_restriction(struct rtattr *attr_list[__NFACCT_MAX],
+ void *user_data)
+{
+ struct counter_arg *carg = (struct counter_arg *)user_data;
+ 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;
+ data_usage_quota du_quota = {0};
+
+ init_nfacct(0, 0, 0, carg, &counter);
+ strncpy(counter.name, cnt_name, sizeof(counter.name)-1);
+ 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_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) {
+ struct nfacct_rule out_counter = counter;
+
+ /* remove old ones, which were with notification */
+ counter.iotype = NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT;
+ ret = del_counter(&counter);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Can't delete restriction%s", counter.name);
+ free(app_id);
+ return ret;
+ }
+ out_counter.iotype = NFACCT_COUNTER_OUT;
+ generate_counter_name(&out_counter);
+ ret = add_iptables_out(&out_counter);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Can't create auxilary counter %s", out_counter.name);
+ free(app_id);
+ return ret;
+ }
+ ret = apply_tethering_restriction(RST_SET);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Can't block tethering");
+ free(app_id);
+ return ret;
+ }
+ }
+
+ /**
+ * 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,
+ quota_id, du_quota.roaming_type, counter.ifname, GLOBAL_CONFIG_IMSI);
+ } else if (counter.intend == NFACCT_WARN) {
+ 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);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Can't delete warning %s", counter.name);
+ free(app_id);
+ return ret;
+ }
+ } else
+ _E("Unkown restriction notification type");
+
+ free(app_id);
+ return 0;
+}
+
+/* notification section */
+static Eina_Bool noti_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ struct counter_arg *carg = (struct counter_arg *)user_data;
+ struct genl ans;
+ struct netlink_serialization_params ser_param = {0};
+ netlink_serialization_command *netlink_command = NULL;
+ int ret;
+
+ _D("nfacct notification");
+ ret = read_netlink(carg->noti_fd, &ans, sizeof(struct genl));
+ if (ret == 0)
+ goto out;
+ carg->ans_len = ret;
+ ser_param.carg = carg;
+ ser_param.ans = &ans;
+ ser_param.eval_attr = fill_restriction;
+ netlink_command = netlink_create_command(&ser_param);
+
+ if (!netlink_command)
+ goto out;
+
+ netlink_command->deserialize_answer(&(netlink_command->params));
+
+out:
+ return ECORE_CALLBACK_RENEW;
+}
+
+static void init_notifier(struct counter_arg *carg)
+{
+ int ret = 0;
+ int option = NFNLGRP_ACCT_QUOTA;
+ struct sockaddr_nl addr;
+ socklen_t addr_len = sizeof(struct sockaddr_nl);
+
+ carg->noti_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
+ ret_msg_if(carg->noti_fd < 0, "Can't create socket");
+
+ /* bind */
+ memset(&addr, 0, sizeof(struct sockaddr_nl));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = 0;
+ addr.nl_pid = 0;
+
+ ret = bind(carg->noti_fd, (struct sockaddr *) &addr, addr_len);
+ ret_msg_if(ret < 0, "Can't bind notification socket");
+
+ ret = getsockname(carg->noti_fd, (struct sockaddr *)&addr, &addr_len);
+ ret_msg_if(ret < 0, "Can't get sockname!");
+
+ ret_msg_if(addr_len != sizeof(struct sockaddr_nl) ||
+ addr.nl_family != AF_NETLINK,
+ "getsockname bad argumetn");
+
+ /* see sock opt */
+ ret = setsockopt(carg->noti_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+ &option, sizeof(int));
+ ret_msg_if(carg->noti_fd < 0, "Can't set sock opt");
+
+ /* register handler */
+ carg->noti_fd_handler = ecore_main_fd_handler_add(
+ carg->noti_fd, ECORE_FD_READ, noti_func_cb,
+ carg, NULL, NULL);
+ ret_msg_if(carg->noti_fd_handler == NULL,
+ "Failed to add noti callbacks\n");
+}
+
+static void fini_notifier(struct counter_arg *carg)
+{
+ shutdown(carg->noti_fd, SHUT_RDWR);
+ ecore_main_fd_handler_del(carg->noti_fd_handler);
+ close(carg->noti_fd);
+}
+
+/* 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;
+}
+
+iface_callback *create_counter_callback(void)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+#define NET_RELEASE_AGENT "/usr/bin/net-cls-release"
+#define NET_CLS_SUBSYS "net_cls"
+
+static int resourced_lcd_on(void *data)
+{
+ display_off = false;
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_lcd_off(void *data)
+{
+ display_off = true;
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_datausage_init(void *data)
+{
+ struct net_counter_opts *net_opts = (struct net_counter_opts *)malloc(
+ sizeof(struct net_counter_opts));
+ struct shared_modules_data *m_data = NULL;
+
+ ret_value_msg_if (net_opts == NULL, RESOURCED_ERROR_OUT_OF_MEMORY,
+ "Not enough memory");
+
+ m_data = get_shared_modules_data();
+ int ret_code;
+
+ memset(net_opts, 0, sizeof(struct net_counter_opts));
+ load_network_opts(net_opts);
+ _D("Initialize network counter function\n");
+ ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
+ "Invalid shared modules data\n");
+
+ set_release_agent(NET_CLS_SUBSYS, NET_RELEASE_AGENT);
+
+ /* register notifier cb */
+ register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
+#ifdef RESOURCED_RESUME
+ register_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_resume_cb);
+#endif
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_srv_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_TERMINATED, app_terminated_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, app_backgrnd_cb);
+ register_notifier(RESOURCED_NOTIFIER_LCD_ON, resourced_lcd_on);
+ register_notifier(RESOURCED_NOTIFIER_LCD_OFF, resourced_lcd_off);
+ register_notifier(RESOURCED_NOTIFIER_APP_WAKEUP, resourced_app_wakeup);
+ register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, resourced_app_foregrd);
+ m_data->carg = init_counter_arg(net_opts);
+ ret_code = resourced_iface_init();
+ ret_value_msg_if(ret_code < 0, ret_code, "resourced_iface_init failed");
+ 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);
+ reactivate_restrictions();
+ create_net_background_cgroup(m_data->carg);
+ 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);
+
+ /* let's make a notification socket */
+ init_notifier(m_data->carg);
+ set_change_allow_cb(handle_change_allowance);
+#endif
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_datausage_finalize(void *data)
+{
+ struct shared_modules_data *m_data = get_shared_modules_data();
+
+ _D("Finalize network counter function\n");
+ resourced_remove_vconf_datausage_cb();
+ ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
+ "Invalid shared modules data\n");
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+ remove_whole_nf_counters(m_data->carg);
+ fini_notifier(m_data->carg);
+#endif
+ resourced_finalize_counter_func(m_data->carg);
+ free(m_data->carg->opts);/* allocation was in resourced_datausage_init */
+ ecore_timer_del(m_data->carg->erase_timer);
+ finalize_carg(m_data->carg);
+ finalize_storage_stm();
+ unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
+#ifdef RESOURCED_RESUME
+ unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_resume_cb);
+#endif
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_srv_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATED, app_terminated_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, app_backgrnd_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_LCD_ON, resourced_lcd_on);
+ unregister_notifier(RESOURCED_NOTIFIER_LCD_OFF, resourced_lcd_off);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_WAKEUP, resourced_app_wakeup);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, resourced_app_foregrd);
+ resourced_iface_finalize();
+ finalize_iftypes();
+ finilize_telephony();
+
+ return RESOURCED_ERROR_NONE;
+}
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+
+static int compare_nfcntr(gconstpointer a, gconstpointer b,
+ gpointer UNUSED user_data)
+{
+ struct nfacct_key *key_a = (struct nfacct_key *)a;
+ struct nfacct_key *key_b = (struct nfacct_key *)b;
+ int ret = key_a->classid - key_b->classid;
+
+ if (ret)
+ return ret;
+ ret = key_a->iftype - key_b->iftype;
+ if (ret)
+ return ret;
+ ret = key_a->iotype - key_b->iotype;
+ if (ret)
+ return ret;
+
+ 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, free, free);
+}
+
+static struct nfacct_value *lookup_counter(struct nfacct_rule *counter)
+{
+ struct nfacct_key key = {
+ .classid = counter->classid,
+ .iftype = counter->iftype,
+ .iotype = counter->iotype,
+ .intend = counter->intend,
+ };
+
+ STRING_SAVE_COPY(key.ifname, counter->ifname);
+
+ if (!counter->carg)
+ return NULL;
+ return (struct nfacct_value *)g_tree_lookup(counter->carg->nf_cntrs,
+ &key);
+}
+
+/* Called only in case of successful kernel operation */
+void keep_counter(struct nfacct_rule *counter)
+{
+ struct nfacct_key *key = NULL;
+ struct nfacct_value *value = lookup_counter(counter);
+
+ 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));
+
+ 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);
+ }
+
+ 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;
+ if (counter->iftype == RESOURCED_IFACE_DATACALL)
+ value->imsi = get_imsi_hash(get_current_modem_imsi());
+
+#ifdef NETWORK_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 NETWORK_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 /* NETWORK_DEBUG_ENABLED */
+
+ if (!value) {
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("Can't find counter", counter->name);
+#endif
+ return;
+ }
+ if (!CHECK_BIT(value->fini, NFACCT_FINAL_REMOVE)) {
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("No need to remove value %p", value);
+#endif
+ return;
+ }
+#ifdef NETWORK_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 NETWORK_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 NETWORK_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) {
+#ifdef DEBUG_ENABLED
+ _D("We didn't find this restriction in list! Create new one!");
+#endif
+ info = (resourced_restriction_info *)malloc(sizeof(resourced_restriction_info));
+ if (!info) {
+ _E("Can't allocate memory");
+ free(app_id);
+ return FALSE;
+ }
+ memset(info, 0, sizeof(resourced_restriction_info));
+ *rst_list = g_slist_prepend(*rst_list, info);
+ }
+
+#ifdef NETWORK_DEBUG_ENABLED
+ if (nf_key->intend == NFACCT_BLOCK)
+ _D("%p rst_state: %d", nf_value, nf_value->rst_state);
+#endif
+
+ 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 = strndup(nf_key->ifname, strlen(nf_key->ifname));
+ if (!info->imsi && nf_value->imsi)
+ info->imsi = strndup(nf_value->imsi, strlen(nf_value->imsi));
+ if (!info->app_id)
+ info->app_id = app_id;
+ else
+ free(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)key;
+ char *ifname = (char *)value;
+ struct nfacct_value *counter = NULL;
+
+ if (iftype <= RESOURCED_IFACE_UNKNOWN ||
+ 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 ||
+ (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");
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void populate_incomplete_counter(void *data)
+{
+ struct make_rule_context *ctx = (struct make_rule_context *)data;
+ generate_counter_name(ctx->counter);
+
+ keep_counter(ctx->counter);
+}
+
+static resourced_ret_c create_iptables_rule(const char *app_id, const pid_t pid)
+{
+ struct shared_modules_data *m_data = get_shared_modules_data();
+ struct counter_arg *carg = m_data->carg;
+ struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
+ struct make_rule_context ctx;
+ uint32_t classid = get_classid_by_app_id(app_id, false);
+
+ ctx.carg = carg;
+ ctx.counter = &counter;
+ init_nfacct(classid, pid, NFACCT_COUNTER_IN, carg, &counter);
+
+ 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_ifnames((ifnames_iterator)create_each_iptable_rule,
+ populate_incomplete_counter, &ctx);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/* iface reset section */
+struct iftype_context {
+ resourced_iface_type iftype;
+ struct iptc_holder *iptc_holder;
+ struct counter_arg *carg;
+};
+
+static void trace_nf_key_value(struct nfacct_key *nfacct_key, struct nfacct_value *nfacct_value)
+{
+#ifdef NETWORK_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 /* NETWORK_DEBUG_ENABLED */
+}
+
+static bool is_incomplete_counter(struct nfacct_key *nfacct_key,
+ struct nfacct_value *nfacct_value)
+{
+ return nfacct_key->iftype == RESOURCED_IFACE_UNKNOWN &&
+ nfacct_value->state == NFACCT_STATE_ACTIVE;
+ /* special incomplete status unnecessary */
+}
+
+/*
+ * skip activating in case of
+ * 1. it's not a counter, it's warning or block restriction
+ * 2. new interface is the same as was before, but in this case
+ * no need to skip in case of incomplete counter
+ * 3. counting is not allowed, e.g. by security reason
+ */
+static bool check_skip_activating(struct nfacct_key *nfacct_key,
+ struct nfacct_value *nfacct_value,
+ struct iftype_context *ctx)
+{
+ /**
+ * 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 true;
+ }
+ /**
+ * skip if it's the same interface,
+ * and we have active counter for it,
+ * e.g from SIM1 to SIM2,
+ */
+ if (ctx->iftype == nfacct_key->iftype &&
+ nfacct_value->state == NFACCT_STATE_ACTIVE &&
+ !is_incomplete_counter(nfacct_key, nfacct_value))
+ return true;
+
+ if (!is_counting_allowed(nfacct_key->iftype))
+ return true;
+
+ return false;
+}
+
+static gboolean activate_each_counter_by_iftype(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ struct nfacct_key *nfacct_key = (struct nfacct_key *)key;
+ struct nfacct_value *nfacct_value = (struct nfacct_value *)value;
+ struct iftype_context *ctx = (struct iftype_context *)data;
+ struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
+ struct nfacct_value *found_counter;
+ int ret = RESOURCED_ERROR_NONE;
+
+
+ trace_nf_key_value(nfacct_key, nfacct_value);
+
+ if (check_skip_activating(nfacct_key, nfacct_value, ctx)) {
+ /**
+ * it means counting is not allowed
+ * or ctx->iftype was activated, but we still have
+ * active counter for another interface, assume
+ * WLAN is preffered, so lets deactivate it
+ */
+ _D("skip activating");
+ return FALSE; /* continue iteration */
+ }
+
+ /**
+ * counter still active it wasn't yet removed,
+ * del_counter_delayed isn't yet called,
+ * following code not in check_skip_activating,
+ * due it modifies tree value
+ */
+ 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;
+ counter.iftype = ctx->iftype;
+ counter.carg = ctx->carg;
+
+ generate_counter_name(&counter);
+
+ found_counter = lookup_counter(&counter);
+ ret_value_msg_if(found_counter != NULL &&
+ found_counter->state == NFACCT_STATE_ACTIVE, FALSE,
+ "Counter already exists and active!");
+
+ ret = resourced_ipt_prepend(&counter, ctx->iptc_holder->iptc);
+
+ if (ret != RESOURCED_ERROR_NONE)
+ return FALSE;
+
+ if (found_counter != NULL && found_counter->state ==
+ NFACCT_STATE_DEACTIVATED)
+ found_counter->state = NFACCT_STATE_ACTIVE;
+
+ return FALSE;
+}
+
+static void turn_on_counters(resourced_iface_type iftype, struct counter_arg *carg)
+{
+ struct iftype_context ctx;
+ struct ipt_context iptc = {0};
+ struct iptc_holder iptc_holder = {0};
+ resourced_ret_c ret;
+ ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
+ "Can't get iftype for remove counter");
+
+ ret = resourced_ipt_begin(&iptc);
+ ret_msg_if(ret != RESOURCED_ERROR_NONE, "Could not produce iptables rule inclusions");
+
+ ctx.iftype = iftype;
+ ctx.carg = carg;
+ iptc_holder.iptc = &iptc;
+ ctx.iptc_holder = &iptc_holder;
+ g_tree_foreach(ctx.carg->nf_cntrs, activate_each_counter_by_iftype, &ctx);
+ ret = resourced_ipt_commit(&iptc);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("iptables commit was failed");
+}
+
+static void turn_off_counters(resourced_iface_type iftype, struct counter_arg *carg)
+{
+ struct iftype_context ctx;
+ struct iptc_holder *holder;
+
+ ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
+ "Can't get iftype for remove counter");
+
+ /* should exists even in timer */
+ holder = (struct iptc_holder *)malloc(sizeof(struct iptc_holder));
+ ret_msg_if(holder == NULL, "Not enough memory");
+ memset(holder, 0, sizeof(struct iptc_holder));
+ ctx.iftype = iftype;
+ ctx.carg = carg;
+ ctx.iptc_holder = holder;
+ g_tree_foreach(ctx.carg->nf_cntrs, deactivate_each_counter_by_iftype, &ctx);
+}
+
+static void handle_on_iface_up(const int ifindex)
+{
+ /* NEW IFACE LET's add COUNTER if it DOESN"T exists */
+ resourced_iface_type iftype;
+ struct shared_modules_data *m_data;
+ m_data = get_shared_modules_data();
+ ret_msg_if(m_data == NULL,
+ "Can't get module data!");
+ iftype = get_iftype(ifindex);
+ turn_on_counters(iftype, m_data->carg);
+ add_one_tizen_os_counter((gpointer)iftype, NULL, m_data->carg);
+}
+
+static void handle_change_allowance(resourced_iface_type iftype, bool enabled)
+{
+ struct shared_modules_data *m_data;
+ m_data = get_shared_modules_data();
+ ret_msg_if(m_data == NULL,
+ "Can't get module data!");
+ if (enabled)
+ turn_on_counters(iftype, m_data->carg);
+ else
+ turn_off_counters(iftype, m_data->carg);
+}
+
+struct del_counter_context
+{
+ struct nfacct_value *nfacct_value;
+ struct nfacct_key *nfacct_key;
+ struct counter_arg *carg;
+ struct iptc_holder *iptc_holder;
+};
+
+static Eina_Bool del_counter_delayed(void *data)
+{
+ int ret;
+ struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
+ struct del_counter_context *del_ctx = (struct del_counter_context *)data;
+ struct nfacct_value *nfacct_value;
+ struct nfacct_key *nfacct_key;
+
+ if (!del_ctx) {
+ _E("Invalid Input parameter!");
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ nfacct_value = del_ctx->nfacct_value;
+ nfacct_key = del_ctx->nfacct_key;
+ if (nfacct_value->state != NFACCT_STATE_DEL_DELAYED) {
+ _D("nfacct counter state is %d", nfacct_value->state);
+ goto finalize_iptc;
+ }
+
+ if (!del_ctx->iptc_holder)
+ goto out;
+
+ if (del_ctx->iptc_holder != NULL && del_ctx->iptc_holder->iptc == NULL) {
+ del_ctx->iptc_holder->iptc = (struct ipt_context *)malloc(sizeof(struct ipt_context));
+ if (del_ctx->iptc_holder->iptc == NULL) {
+ _E("not enough memory!");
+ goto out;
+ }
+ memset(del_ctx->iptc_holder->iptc, 0, sizeof(struct ipt_context));
+ resourced_ipt_begin(del_ctx->iptc_holder->iptc);
+ }
+
+ counter.classid = nfacct_key->classid;
+ counter.iotype = nfacct_key->iotype;
+ counter.iftype = nfacct_key->iftype;
+ counter.carg = del_ctx->carg;
+ STRING_SAVE_COPY(counter.ifname, nfacct_key->ifname);
+
+ generate_counter_name(&counter);
+
+ ret = resourced_ipt_remove(&counter, del_ctx->iptc_holder->iptc);
+
+ if(ret != RESOURCED_ERROR_NONE) {
+ _E("Can't delete counter %s",
+ counter.name);
+ goto finalize_iptc;
+ }
+
+ nfacct_value->state = NFACCT_STATE_DEACTIVATED;
+
+finalize_iptc:
+ del_ctx->iptc_holder->count_down -= 1;
+
+ if (del_ctx->iptc_holder->count_down == 0 && del_ctx->iptc_holder->iptc) {
+ resourced_ipt_commit(del_ctx->iptc_holder->iptc);
+ free(del_ctx->iptc_holder->iptc);
+ free(del_ctx->iptc_holder);
+ }
+
+out:
+
+ free(del_ctx);
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static gboolean deactivate_each_counter_by_iftype(gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ struct nfacct_key *nfacct_key = (struct nfacct_key *)key;
+ struct nfacct_value *nfacct_value = (struct nfacct_value *)value;
+ struct iftype_context *ctx = (struct iftype_context *)data;
+ struct del_counter_context *del_ctx = NULL;
+
+ /**
+ * 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(
+ sizeof(struct del_counter_context));
+ ret_value_msg_if(del_ctx == NULL, FALSE,
+ "Can't allocate del_counter_context");
+ del_ctx->nfacct_key = nfacct_key;
+ del_ctx->nfacct_value = nfacct_value;
+ del_ctx->carg = ctx->carg;
+ ctx->iptc_holder->count_down++;
+ del_ctx->iptc_holder = ctx->iptc_holder;
+ nfacct_value->state = NFACCT_STATE_DEL_DELAYED;
+ ecore_timer_add(DELAY_DELETE_INVERVAL, del_counter_delayed, del_ctx);
+
+ return FALSE;
+}
+
+static void handle_on_iface_down(const int ifindex)
+{
+ /* iface is gone, lets remove counter */
+ resourced_iface_type iftype;
+ struct shared_modules_data *m_data;
+ m_data = get_shared_modules_data();
+ ret_msg_if(m_data == NULL,
+ "Can't get module data!");
+ iftype = get_iftype(ifindex);
+
+ turn_off_counters(iftype, m_data->carg);
+}
+
+iface_callback *create_counter_callback(void)
+{
+ iface_callback *ret_arg = (iface_callback *)
+ malloc(sizeof(iface_callback));
+
+ if (!ret_arg) {
+ _E("Malloc of iface_callback failed\n");
+ return NULL;
+ }
+ ret_arg->handle_iface_up = handle_on_iface_up;
+ ret_arg->handle_iface_down = handle_on_iface_down;
+
+ return ret_arg;
+}
+
+/* end iface reset section */
+
+#endif /*DATAUSAGE_TYPE*/
+
+resourced_ret_c join_net_cls(const char *app_id, const pid_t pid)
+{
+ resourced_ret_c ret;
+ 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);
+#ifdef CONFIG_DATAUSAGE_NFACCT
+ /* Create iptable rule */
+ ret = create_iptables_rule(app_id, pid);
+ ret_value_if(ret != RESOURCED_ERROR_NONE, ret);
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops datausage_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "datausage",
+ .init = resourced_datausage_init,
+ .exit = resourced_datausage_finalize,
+};
+
+MODULE_REGISTER(&datausage_modules_ops)
--- /dev/null
+/*
+ * 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 datausage-quota-processing.c
+ *
+ * @desc Quota processing implementation.
+ * This implementation updates used quota table and determine
+ * moment of time for blocking.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sqlite3.h>
+#include <inttypes.h>
+
+#include "counter.h"
+#include "database.h"
+#include "data_usage.h"
+#include "macro.h"
+#include "resourced.h"
+#include "net-cls-cgroup.h"
+#include "notification.h"
+#include "storage.h"
+#include "trace.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;
+static sqlite3_stmt *insert_stmt;
+static sqlite3_stmt *clear_effective_stmt;
+
+static const char select_query[] = "SELECT qt.binpath, qt.sent_quota, qt.rcv_quota, "\
+ "qt.snd_warning_threshold, qt.rcv_warning_threshold, "\
+ "sent_used_quota, rcv_used_quota, qt.start_time AS quota_start_time, "\
+ "qt.time_period AS quota_period, efq.start_time AS effective_start, "\
+ "efq.finish_time AS effective_finish, qt.iftype AS iftype, " \
+ "qt.roaming, "\
+ "efq.state, "\
+ "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.imsi = efq.imsi) "\
+ "GROUP BY qt.binpath, qt.iftype, qt.sent_quota, qt.rcv_quota, " \
+ "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, imsi) " \
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+static const char clear_effective_quota_query[] = "DELETE FROM effective_quotas " \
+ " WHERE binpath = ? AND iftype = ? AND roaming = ? AND imsi = ?";
+
+enum resourced_quota_state {
+ RESOURCED_QUOTA_UNKNOWN, /**< undefined/initial state */
+ RESOURCED_QUOTA_APPLIED, /**< enabled/applied state */
+ RESOURCED_QUOTA_REVERTED, /**< disabled/reverted state */
+};
+
+struct quota {
+ int quota_id;
+ int64_t send_quota;
+ int64_t rcv_quota;
+ int64_t sent_used_quota;
+ int64_t rcv_used_quota;
+ int snd_warning_threshold;
+ int rcv_warning_threshold;
+ int start_time;
+ int time_period;
+ int real_start;
+ int real_finish;
+ enum resourced_quota_state state;
+};
+
+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 {
+ DROP_UNDEF = 0,
+ DROP_NEED = 1,
+ DROP_NO_NEED = 2
+} drop_decision;
+
+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;
+
+ 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 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 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;
+ }
+
+ key->app_id = strndup((char *)sqlite3_column_text(query, 0),
+ strlen((char *)sqlite3_column_text(query, 0)));
+ key->iftype = sqlite3_column_int(
+ query, 11);
+ key->roaming = sqlite3_column_int(
+ query, 12);
+ key->imsi_hash = strndup((char *)sqlite3_column_text(query, 15),
+ strlen((char *)sqlite3_column_text(query, 15)));
+ key->ground = sqlite3_column_int(query, 16);
+
+ value->send_quota = sqlite3_column_int64(
+ query, 1);
+ value->rcv_quota = sqlite3_column_int64(
+ query, 2);
+ value->snd_warning_threshold = sqlite3_column_int64(
+ query, 3);
+ value->rcv_warning_threshold = sqlite3_column_int64(
+ query, 4);
+ value->sent_used_quota = sqlite3_column_int64(
+ query, 5);
+ value->rcv_used_quota = sqlite3_column_int64(
+ query, 6);
+ value->start_time = sqlite3_column_int64(
+ query, 7);
+ value->time_period = sqlite3_column_int64(
+ query, 8);
+ value->real_start = sqlite3_column_int64(
+ query, 9);
+ value->real_finish = sqlite3_column_int64(
+ query, 10);
+ value->state = sqlite3_column_int64(
+ query, 13);
+ value->quota_id = sqlite3_column_int(
+ query, 14);
+#ifdef DEBUG_ENABLED
+ _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);
+#endif
+ g_tree_insert(quotas, key, value);
+ }
+ } while (rc == SQLITE_ROW);
+
+ return RESOURCED_ERROR_NONE;
+free_value:
+ g_free(value);
+ return ret;
+}
+
+static gint compare_quota_key(gconstpointer a, gconstpointer b,
+ gpointer UNUSED user_data)
+{
+ const struct quota_key *key1 = a;
+ const struct quota_key *key2 = b;
+ 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;
+}
+
+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
+
+void clear_effective_quota(const char *app_id,
+ const resourced_iface_type iftype,
+ const resourced_roaming_type roaming,
+ const char *imsi_hash)
+{
+ if (sqlite3_bind_text(clear_effective_stmt, 1, app_id, -1,
+ SQLITE_TRANSIENT) != SQLITE_OK) {
+ _SE("Can not bind app_id:%s for preparing statement:%s",
+ app_id, sqlite3_errmsg(resourced_get_database()));
+ return;
+ }
+
+ if (sqlite3_bind_int(clear_effective_stmt, 2, iftype)
+ != SQLITE_OK) {
+ _E("Can not bind iftype:%d for preparing statement:%s",
+ iftype, sqlite3_errmsg(resourced_get_database()));
+ return;
+ }
+
+ if (sqlite3_bind_int(clear_effective_stmt, 3, roaming)
+ != SQLITE_OK) {
+ _E("Can not bind roaming:%d for preparing statement:%s",
+ roaming, sqlite3_errmsg(resourced_get_database()));
+ 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()));
+ sqlite3_reset(clear_effective_stmt);
+}
+
+static inline int _is_period_devisible(const int time_period,
+ data_usage_quota_period_t quota_period)
+{
+ return time_period > quota_period &&
+ time_period % RESOURCED_PERIOD_MONTH == 0;
+}
+
+/**
+ * @desc Define period base on stored in data base time interval
+ * @return time period
+ */
+static data_usage_quota_period_t _define_period(const int time_period, int *quantity)
+{
+ if (quantity == 0)
+ return RESOURCED_PERIOD_UNDEF;
+
+ if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
+ *quantity = time_period / RESOURCED_PERIOD_MONTH;
+ return RESOURCED_PERIOD_MONTH;
+ }
+
+ if (_is_period_devisible(time_period, RESOURCED_PERIOD_MONTH)) {
+ *quantity = time_period / RESOURCED_PERIOD_MONTH;
+ return RESOURCED_PERIOD_MONTH;
+ }
+
+ if (_is_period_devisible(time_period, RESOURCED_PERIOD_WEEK)) {
+ *quantity = time_period / RESOURCED_PERIOD_WEEK;
+ return RESOURCED_PERIOD_WEEK;
+ }
+
+ if (_is_period_devisible(time_period, RESOURCED_PERIOD_DAY)) {
+ *quantity = time_period / RESOURCED_PERIOD_DAY;
+ return RESOURCED_PERIOD_DAY;
+ }
+
+ if (_is_period_devisible(time_period, RESOURCED_PERIOD_HOUR)) {
+ *quantity = time_period / RESOURCED_PERIOD_HOUR;
+ return RESOURCED_PERIOD_HOUR;
+ }
+
+ *quantity = time_period;
+ return RESOURCED_PERIOD_UNDEF;
+}
+
+
+static time_t get_finish_time(const time_t start_time, const int time_period)
+{
+ int quantity = 0;
+ struct tm new_start;
+
+ if (gmtime_r((const time_t *)&start_time, &new_start) == NULL)
+ return (time_t)(-1);
+
+ switch (_define_period(time_period, &quantity)) {
+ case RESOURCED_PERIOD_UNDEF:
+ return start_time + time_period;
+ case RESOURCED_PERIOD_HOUR:
+ new_start.tm_hour += quantity;
+ break;
+ case RESOURCED_PERIOD_DAY:
+ new_start.tm_mday += quantity;
+ break;
+ case RESOURCED_PERIOD_WEEK:
+ new_start.tm_mday += quantity * 7;
+ break;
+ case RESOURCED_PERIOD_MONTH:
+ new_start.tm_mon += quantity;
+ break;
+ }
+
+ /* normilize */
+ return mktime(&new_start);
+}
+
+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,
+ void *user_data)
+{
+ struct data_usage_context *context =
+ (struct data_usage_context *)user_data;
+
+ ret_value_msg_if(!context, RESOURCED_CONTINUE,
+ "Invalid cb data!");
+
+ if (context->roaming != info->roaming)
+ return RESOURCED_CONTINUE;
+
+ /* 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,
+ const struct quota *app_quota)
+{
+ if (!key || !app_quota) {
+ _E("Please, provide valid argument.");
+ return;
+ }
+
+ if (!app_quota->sent_used_quota &&
+ !app_quota->rcv_used_quota) {
+ _D("Nothing to store for effective quota.");
+ return;
+ }
+
+ if (sqlite3_bind_text(insert_stmt, 1, key->app_id, -1,
+ SQLITE_STATIC) != SQLITE_OK) {
+ _SE("Can not bind app_id:%s for preparing statement",
+ key->app_id);
+ return;
+ }
+
+ if (sqlite3_bind_int64(insert_stmt, 2, app_quota->sent_used_quota)
+ != SQLITE_OK) {
+ _E("Can not bind sent_used_quota:%lld for preparing statement",
+ app_quota->sent_used_quota);
+ return;
+ }
+
+ if (sqlite3_bind_int64(insert_stmt, 3, app_quota->rcv_used_quota)
+ != SQLITE_OK) {
+ _E("Can not bind rcv_used_quota:%lld for preparing statement",
+ app_quota->rcv_used_quota);
+ return;
+ }
+
+ if (sqlite3_bind_int64(insert_stmt, 4, app_quota->real_start)
+ != SQLITE_OK) {
+ _E("Can not bind start_time:%d for preparing statement",
+ app_quota->real_start);
+ return;
+ }
+
+ if (sqlite3_bind_int64(insert_stmt, 5, app_quota->real_finish)
+ != SQLITE_OK) {
+ _E("Can not bind finish_time:%d for preparing statement",
+ app_quota->real_finish);
+ return;
+ }
+
+ if (sqlite3_bind_int(insert_stmt, 6, key->iftype)
+ != SQLITE_OK) {
+ _E("Can not bind iftype:%d for preparing statement",
+ key->iftype);
+ return;
+ }
+
+ if (sqlite3_bind_int(insert_stmt, 7, key->roaming)
+ != SQLITE_OK) {
+ _E("Can not bind roaming:%d for preparing statement",
+ key->roaming);
+ return;
+ }
+
+ if (sqlite3_bind_int(insert_stmt, 8, app_quota->state)
+ != SQLITE_OK) {
+ _E("Can not bind state:%d for preparing statement",
+ app_quota->state);
+ 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 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 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,};
+ const time_t cur_time = time(0);
+ char buf[30];
+
+ app_id = !strcmp(app_id, RESOURCED_ALL_APP) ? 0: app_id;
+
+ if (cur_time < start_time) {
+ _D("No need to update effective quota!");
+ return;
+ }
+
+ out_context.roaming = roaming;
+ 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;
+
+ if (data_usage_details_foreach(app_id, &rule, data_usage_details_cb,
+ &out_context) != RESOURCED_ERROR_NONE) {
+ _E("Cant obtain sent_used_quota/rcv_used_quota");
+ return;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (ctime_r(&rule.from, buf))
+ _SD("Get counted traffic for appid:%s, per %s "\
+ "time interval %d, incoming:%" PRId64 ", outgoing:%" PRId64 "", app_id,
+ buf, time_period, out_context.rcv_used_quota,
+ out_context.sent_used_quota);
+#endif
+ 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);
+}
+
+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;
+ }
+
+ 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, %s", app_id,
+ ser_quota->iftype, ser_quota->imsi_hash);
+ /* Restrictions can't be separated */
+ 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 = strndup(app_id, strlen(app_id));
+ if (!insert_key->app_id) {
+ _E("not enough memory");
+ goto release_quota_value;
+ }
+
+ insert_key->imsi_hash = strndup(ser_quota->imsi_hash, strlen(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 = strndup(imsi_hash, strlen(imsi_hash));
+
+ g_tree_remove(quotas, (gconstpointer*)(&key));
+}
+
+
+static resourced_ret_c _init_quotas(void)
+{
+ quotas = g_tree_new_full(compare_quota_key, NULL,
+ quota_key_destructor, quota_destructor);
+
+ if (!resourced_get_database())
+ return RESOURCED_ERROR_DB_FAILED;
+
+ if (select_stmt && insert_stmt)
+ return RESOURCED_ERROR_NONE;
+
+ if (sqlite3_prepare_v2(resourced_get_database(),
+ select_query, -1, &select_stmt,
+ NULL) != SQLITE_OK) {
+ _E("Error preparing query: %s, \
+ %s\n", select_query, sqlite3_errmsg(resourced_get_database()));
+ goto handle_error;
+ }
+
+ if (sqlite3_prepare_v2(resourced_get_database(),
+ insert_query, -1, &insert_stmt,
+ NULL) != SQLITE_OK) {
+ _E("Error preparing query: %s, \
+ %s\n", insert_query, sqlite3_errmsg(resourced_get_database()));
+ goto handle_error;
+ }
+
+ if (sqlite3_prepare_v2(resourced_get_database(),
+ clear_effective_quota_query,
+ -1, &clear_effective_stmt,
+ NULL) != SQLITE_OK) {
+ _E("Error preparing query: %s, \
+ %s\n", clear_effective_quota_query,
+ sqlite3_errmsg(resourced_get_database()));
+ goto handle_error;
+ }
+
+ return RESOURCED_ERROR_NONE;
+handle_error:
+ /* Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op */
+ sqlite3_finalize(select_stmt);
+ sqlite3_finalize(insert_stmt);
+ sqlite3_finalize(clear_effective_stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+}
+
+
+/**
+ * Update quotas tree, where app_id will the key
+ */
+static resourced_ret_c load_quotas(void)
+{
+ const resourced_ret_c ret = _init_quotas();
+ 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] = {
+ 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] = {
+ 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 */
+};
+
+/*
+ * @desc this function returns valud per second
+ */
+static int64_t _get_quota_gap(const resourced_iface_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];
+
+ if (iftype > RESOURCED_IFACE_UNKNOWN &&
+ iftype < RESOURCED_IFACE_ALL)
+ return quota_gap_value[iftype];
+
+ return quota_gap_value[RESOURCED_IFACE_UNKNOWN];
+}
+
+static int check_restriction_needed(const int64_t send_delta,
+ const int64_t rcv_delta,
+ const resourced_iface_type iftype,
+ int update_period)
+{
+ /* multiply on 2, due */
+ const int64_t quota_gap = _get_quota_gap(iftype) * update_period;
+
+ _D("send_delta %"PRId64" rcv_delta%"PRId64" quota_gap %"PRId64""
+ "update_period %d ",
+ send_delta, rcv_delta, quota_gap, update_period);
+ return send_delta <= quota_gap ||
+ rcv_delta <= quota_gap;
+}
+
+inline static int get_warning_limit(int64_t delta, int64_t limit, int threshold)
+{
+ if (delta < threshold)
+ return 0; /* send warning immediately */
+
+ if (limit < threshold) {
+ _E("Warning threshold is greater than limit!");
+ return WARNING_THRESHOLD_DEFAULT; /* 0 means kernel will
+ not procced it*/
+ }
+ return limit - threshold;
+}
+
+static int cast_restriction_limit(int64_t delta)
+{
+ if (delta < 0)
+ return 0;
+ if (delta > INT_MAX)
+ return INT_MAX;
+ 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;
+ struct counter_arg *carg = (struct counter_arg *)user_data;
+ resourced_net_restrictions rst = { RESOURCED_STATE_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 net_counter_opts *opts;
+
+ ret_value_msg_if(!carg, FALSE, "Please provide valid carg argument!");
+
+ opts = carg->opts;
+
+ if (skip_quota(key_quota, app_quota, send_delta, rcv_delta)) {
+ _D("no need to apply quota");
+ return FALSE;
+ }
+#ifdef DEBUG_ENABLED
+ _D("quota rcv: %" PRId64 ", send: %" PRId64 "", app_quota->rcv_quota,
+ app_quota->send_quota);
+ _D("delta rcv: %" PRId64 ", send: %" PRId64 "", rcv_delta, send_delta);
+#endif
+ /* 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) ||
+ app_quota->rcv_warning_threshold < app_quota->rcv_used_quota) &&
+ (key_quota->roaming == RESOURCED_ROAMING_UNKNOWN ||
+ 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(send_delta,
+ rst.send_limit, app_quota->snd_warning_threshold);
+ if (app_quota->rcv_warning_threshold <= app_quota->rcv_used_quota)
+ rst.rcv_warning_limit = 0;
+ else
+ rst.rcv_warning_limit = app_quota->rcv_warning_threshold;
+
+#ifdef DEBUG_ENABLED
+ _SD("Applying gap quota for %s, iftype %d, ground", key_quota->app_id,
+ key_quota->iftype, key_quota->ground);
+#endif
+ rst.iftype = key_quota->iftype;
+ rst.ifname = get_iftype_name(rst.iftype);
+ rst.roaming = key_quota->roaming;
+ rst.imsi = key_quota->imsi_hash;
+
+ /*
+ * 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, false, RESOURCED_RESTRICTION_ACTIVATED) != 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(struct counter_arg *carg)
+{
+ 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 bool check_ground_state(const struct quota_key *qkey,
+ struct application_stat *app_stat)
+{
+ /*
+ * unspecified quota, means BACKGROUND and FOREGROUND
+ * traffic should be taken into account
+ * */
+ if (qkey->ground == RESOURCED_STATE_UNKNOWN)
+ return true;
+ return CHECK_BIT(qkey->ground, app_stat->ground);
+}
+
+static gboolean update_pseudo_app_entry(gpointer key,
+ gpointer value, gpointer user_data)
+{
+ struct update_all_arg *arg = (struct
+ 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;
+ }
+#ifdef DEBUG_ENABLED
+ _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);
+#endif
+ /* 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) &&
+ check_ground_state(qkey, arg->app_stat)) ||
+ !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;
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d \n",
+ total_quota->sent_used_quota, total_quota->rcv_used_quota,
+ arg->iftype);
+ _D("app id %s", qkey->app_id);
+#endif
+ }
+
+ return FALSE;
+}
+
+static void update_all_app_quotas(struct update_all_arg *update_all_arg)
+{
+ /* Now RESOURCED_ALL_APP can contain many iftypes */
+ g_tree_foreach(quotas, update_pseudo_app_entry, update_all_arg);
+}
+
+static void update_traffic_quota(const struct quota_key *quota_key,
+ uint32_t *snd_count,
+ uint32_t *rcv_count)
+{
+ struct quota *found_quota = g_tree_lookup(quotas, quota_key);
+
+ if (!found_quota)
+ return;
+ if (time(0) < found_quota->start_time) {
+ _D("No need to update effective quota!");
+ return;
+ }
+ found_quota->sent_used_quota += *snd_count;
+ found_quota->rcv_used_quota += *rcv_count;
+#ifdef DEBUG_ENABLED
+ _D("update total_quota tx:%"PRId64";rx:%"PRId64"\n",
+ found_quota->sent_used_quota, found_quota->rcv_used_quota);
+
+ _D("delta_rcv %d app_id %s\n", *rcv_count, quota_key->app_id);
+#endif
+ *snd_count = 0;
+ *rcv_count = 0;
+ return;
+}
+
+static gboolean update_each_quota(gpointer key, gpointer value,
+ gpointer UNUSED userdata)
+{
+ const struct classid_iftype_key *app_key =
+ (const struct classid_iftype_key *)key;
+ struct application_stat *app_stat =
+ (struct application_stat *)value;
+ struct update_all_arg arg = {
+ .iftype = app_key->iftype,
+ .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 */
+ update_all_app_quotas(&arg);
+
+ if (!app_stat->application_id)
+ return FALSE;
+
+ qkey.app_id = app_stat->application_id;
+ qkey.iftype = app_key->iftype;
+ qkey.roaming = app_stat->is_roaming;
+ /* TODO ground is not handled for quota per application,
+ * due GUI is not set quota per application, yet,
+ * no such requirements */
+ 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;
+}
+
+static void actualize_quota_table(struct application_stat_tree *apps)
+{
+ g_tree_foreach((GTree *)apps->tree, update_each_quota, NULL);
+}
+
+/**
+ * @desc Assume app_quota is not null
+ */
+static void calculate_finish_time(struct quota *app_quota)
+{
+ if (!app_quota || app_quota->real_finish)
+ return;
+
+ if (!app_quota->real_start)
+ app_quota->real_start = time(0);
+
+ app_quota->real_finish = get_finish_time(app_quota->real_start,
+ app_quota->time_period);
+}
+
+/**
+ * @desc Reset quota. This function sets new real_start based on fihish time.
+ * Assume app_quota is set and time(0) < app_quota->real_finish
+ */
+static void reset_quota(struct quota *app_quota)
+{
+#ifdef DEBUG_ENABLED
+ _D("reset_quota called");
+#endif
+ app_quota->real_start = app_quota->real_finish;
+ app_quota->real_finish = 0;
+ app_quota->sent_used_quota = app_quota->rcv_used_quota = 0;
+ restriction_set_status(RESTRICTION_STATE_UNSET);
+}
+
+/**
+ * @desc Remove restriction if needed
+ */
+static void drop_restriction(const struct quota_key *qkey, struct quota *app_quota)
+{
+ if (!app_quota || !qkey) {
+ _E("Please provide valid arguments!");
+ return;
+ }
+
+ /* We can revert only applied quotas */
+ if (app_quota->state != RESOURCED_QUOTA_APPLIED)
+ return;
+#ifdef DEBUG_ENABLED
+ _SD("Removing restriction of quota for %s,%d", qkey->app_id,
+ qkey->iftype);
+#endif
+ 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;
+}
+
+/**
+ * @desc This function actualize current quotas states. It calculate new
+ * finish time and remove restriction if exists.
+ */
+static gboolean flush_quota_node(gpointer key,
+ gpointer value, gpointer UNUSED user_data)
+{
+ struct quota *app_quota = value;
+ struct quota_key *key_quota = key;
+
+ if (!app_quota || !key_quota->app_id)
+ return FALSE; /* continue iteration even
+ current data is empty */
+
+ calculate_finish_time(app_quota);
+
+ _record_quota(key_quota, app_quota);
+ /* It's time to reset */
+ if (time(0) >= app_quota->real_finish) {
+ drop_restriction(key_quota, app_quota);
+ reset_quota(app_quota);
+ }
+ return FALSE;
+}
+
+/**
+ * Save to database effective quota
+ */
+void flush_quota_table(void)
+{
+ g_tree_foreach(quotas, flush_quota_node, NULL);
+}
+
+static void finalize_statement(sqlite3_stmt **stmt)
+{
+ if (*stmt) {
+ sqlite3_finalize(*stmt);
+ *stmt = NULL;
+ }
+}
+
+resourced_ret_c process_quota(struct counter_arg *carg)
+{
+ ret_value_msg_if(!carg, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Please provide carg!");
+
+ 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(carg->result);
+
+ check_and_apply_quota(carg);
+
+ 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
+ */
+void finalize_quotas(void)
+{
+ finalize_statement(&insert_stmt);
+ finalize_statement(&select_stmt);
+ finalize_statement(&clear_effective_stmt);
+ g_tree_destroy(quotas);
+}
+
--- /dev/null
+/*
+ * 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 datausage-quota.c
+ *
+ * @desc Quota logic implementation
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <inttypes.h>
+#include <sqlite3.h>
+#include <string.h>
+#include <time.h>
+#include <vconf.h>
+
+#include "const.h"
+#include "const.h"
+#include "data_usage.h"
+#include "database.h"
+#include "datausage-quota.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "trace.h"
+
+static resourced_ret_c send_quota_message(const char *interface,
+ const char *format_str, char *params[])
+{
+ DBusError err;
+ DBusMessage *msg;
+ resourced_ret_c ret_val;
+ int ret, i = 0;
+
+ do {
+ msg = dbus_method_sync(RESOURCED_DBUS_BUS_NAME, RESOURCED_PATH_NETWORK,
+ RESOURCED_INTERFACE_NETWORK,
+ interface,
+ format_str, 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 RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &ret_val,
+ DBUS_TYPE_INVALID);
+
+ if (ret == FALSE) {
+ _E("no message : [%s:%s]\n", err.name, err.message);
+ ret_val = RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+
+ return ret_val;
+}
+
+static resourced_ret_c send_create_quota_message(const char *app_id,
+ const data_usage_quota *quota)
+{
+ 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);
+ snprintf(rcv_quota, sizeof(rcv_quota), "%" PRId64 "", quota->rcv_quota);
+
+ 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, 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 char *imsi, const resourced_state_t ground)
+{
+ char *params[5];
+
+ serialize_params(params, ARRAY_SIZE(params), app_id, iftype,
+ roaming_type, imsi, ground);
+ return send_quota_message(RESOURCED_NETWORK_REMOVE_QUOTA, "sddsd",
+ params);
+}
+
+API resourced_ret_c remove_datausage_quota(
+ const struct datausage_quota_reset_rule *rule)
+{
+ if (!rule || !rule->app_id)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (rule->iftype <= RESOURCED_IFACE_UNKNOWN ||
+ rule->iftype >= RESOURCED_IFACE_LAST_ELEM)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (rule->roaming < RESOURCED_ROAMING_UNKNOWN ||
+ rule->roaming >= RESOURCED_ROAMING_LAST_ELEM)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ return send_remove_quota_message(rule->app_id, rule->iftype,
+ rule->roaming, rule->imsi ? rule->imsi : "", rule->quota_type);
+}
+
+API resourced_ret_c remove_datausage_quota_by_iftype(
+ const char *app_id, const resourced_iface_type iftype)
+{
+ struct datausage_quota_reset_rule rule = {
+ .app_id = app_id,
+ .iftype = iftype,
+ .roaming = RESOURCED_ROAMING_UNKNOWN,
+ };
+
+ return remove_datausage_quota(&rule);
+}
+
+static int _is_valid_datausage_quota_params(const char *app_id,
+ const data_usage_quota *quota)
+{
+ if (!app_id) {
+ _SE("Empty appid! Please provide valid appid.");
+ return 0;
+ }
+
+ if (!quota) {
+ _E("Empty quota! Please provide valid quota.");
+ return 0;
+ }
+
+ if (quota->iftype >= RESOURCED_IFACE_LAST_ELEM) {
+ _E("Not valid value for iftype! See resourced_iface_type!");
+ return 0;
+ }
+
+ return 1;
+}
+
+static time_t _get_datausage_start_time(const time_t *quota_start_time)
+{
+ return quota_start_time ? *quota_start_time : time(0);
+}
+
+API resourced_ret_c set_datausage_quota(const char *app_id,
+ const data_usage_quota *quota)
+{
+ /* support old behaviour undefined iftype mean all iftype */
+ time_t start_time = 0;
+ data_usage_quota quota_to_send;
+ char buf[28];
+
+ if (!_is_valid_datausage_quota_params(app_id, quota))
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ quota_to_send = *quota;
+ start_time = _get_datausage_start_time(quota->start_time);
+ quota_to_send.start_time = &start_time;
+
+ _SD("quota for app %s set", app_id);
+ _SD("===============================");
+
+ if (ctime_r(quota->start_time, buf) == NULL)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ _SD("quota.start_time = %s", buf);
+ _SD("quota.time_period = %d", quota->time_period);
+ _SD("quota.snd_quota = %lld", quota->snd_quota);
+ _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);
+}
--- /dev/null
+/*
+ * 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: datausage-vconf-callbacks.c
+ *
+ * @desc Add datausage callback functions to vconf
+ *
+ */
+
+#include "const.h"
+#include "counter.h"
+#include "datausage-vconf-callbacks.h"
+#include "datausage-quota-processing.h"
+#include "datausage-quota.h"
+#include "iface.h"
+#include "macro.h"
+#include "resourced.h"
+#include "settings.h"
+#include "trace.h"
+#include "telephony.h"
+#include "notification.h"
+
+#include <stdlib.h>
+#include <vconf.h>
+
+static void wifi_change_cb(keynode_t *key, void *data)
+{
+ int val = vconf_keynode_get_bool(key);
+ _SD("key = %s, value = %d(int)\n",
+ vconf_keynode_get_name(key), val);
+ set_wifi_allowance(val ?
+ RESOURCED_OPTION_ENABLE : RESOURCED_OPTION_DISABLE);
+}
+
+static void datacall_change_cb(keynode_t *key, void *data)
+{
+ int val = vconf_keynode_get_bool(key);
+
+ _SD("key = %s, value = %d(int)\n",
+ vconf_keynode_get_name(key), val);
+ set_datacall_allowance(val ? RESOURCED_OPTION_ENABLE :
+ RESOURCED_OPTION_DISABLE);
+}
+
+static void datacall_logging_change_cb(keynode_t *key, void *data)
+{
+/* TODO introduce set_datacall_logging */
+#if 0
+ struct daemon_opts *options = (struct daemon_opts *)data;
+ int val = vconf_keynode_get_bool(key);
+
+ if (!options) {
+ _E("Please provide valid argument!");
+ return;
+ }
+ _SD("key = %s, value = %d(int)\n",
+ vconf_keynode_get_name(key), val);
+ options->datacall_logging = val ? RESOURCED_OPTION_ENABLE :
+ RESOURCED_OPTION_DISABLE;
+#endif
+}
+
+static void datausage_timer_change_cb(keynode_t *key, void *data)
+{
+ struct net_counter_opts *options = (struct net_counter_opts *)data;
+ int val = vconf_keynode_get_int(key);
+
+ if (!options) {
+ _E("Please provide valid argument!");
+ return;
+ }
+ _SD("key = %s, value = %d(int)\n",
+ vconf_keynode_get_name(key), val);
+
+ 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");
+ ret_msg_if(!carg || !carg->opts,
+ "Please provide valid argument!");
+ vconf_notify_key_changed(RESOURCED_WIFI_STATISTICS_PATH, wifi_change_cb,
+ NULL);
+ vconf_notify_key_changed(RESOURCED_DATACALL_PATH, datacall_change_cb,
+ NULL);
+ vconf_notify_key_changed(RESOURCED_DATAUSAGE_TIMER_PATH,
+ datausage_timer_change_cb, (void *)carg->opts);
+ 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)
+{
+ _D("Remove vconf datausage callbacks\n");
+ vconf_ignore_key_changed(RESOURCED_WIFI_STATISTICS_PATH,
+ wifi_change_cb);
+ vconf_ignore_key_changed(RESOURCED_DATACALL_PATH, datacall_change_cb);
+ vconf_ignore_key_changed(RESOURCED_DATAUSAGE_TIMER_PATH,
+ 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);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#include <resourced.h>
+#include <vconf.h>
+
+#include "trace.h"
+#include "data_usage.h"
+#include "datausage-vconf-common.h"
+
+#ifndef VCONFKEY_SETAPPL_SET_DATA_USAGE_LIMIT_BOOL
+#define VCONFKEY_SETAPPL_SET_DATA_USAGE_LIMIT_BOOL "db/setting/set_data_usage_limit"
+#endif
+
+#ifndef VCONFKEY_SETAPPL_DATA_LIMIT_INT
+#define VCONFKEY_SETAPPL_DATA_LIMIT_INT "db/setting/data_limit"
+#endif
+
+#ifndef VCONFKEY_SETAPPL_DATA_RESTRICTION_INT
+#define VCONFKEY_SETAPPL_DATA_RESTRICTION_INT "db/setting/data_restriction"
+#endif
+
+resourced_ret_c restriction_check_limit_status(int *retval)
+{
+ if (vconf_get_bool(VCONFKEY_SETAPPL_SET_DATA_USAGE_LIMIT_BOOL, retval)) {
+ _E("vconf_get_bool FAIL\n");
+ return RESOURCED_ERROR_FAIL;
+ };
+
+ 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;
+
+ if (vconf_get_int(VCONFKEY_SETAPPL_DATA_RESTRICTION_INT, &limit)) {
+ _E("vconf_get_int FAIL\n");
+ return;
+ }
+
+ if (limit == value) {
+ _E("No need to change a restriction status: %d", limit);
+ return;
+ }
+
+ vconf_set_int(VCONFKEY_SETAPPL_DATA_RESTRICTION_INT, value);
+ return;
+}
--- /dev/null
+/*
+ * 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/resourced/datausage_timer"
+#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);
+ char buf[30];
+
+ until -= ERASE_INTERVAL;
+
+ interval.from = 0;
+ interval.to = until;
+ rule.interval = &interval;
+ if (asctime_r(localtime(&until), buf))
+ _D("Reset datausage statistics till %s", buf);
+ 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;
+}
+
--- /dev/null
+/*
+ * 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 dummy-tethering.c
+ *
+ * @desc Collect here all tethering related function with empty
+ * body
+ */
+
+#include "app-stat.h"
+#include "macro.h"
+#include "transmission.h"
+
+#include "data_usage.h"
+
+#include <vconf.h>
+
+resourced_ret_c apply_tethering_restriction(
+ UNUSED const enum traffic_restriction_type type)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+void tethering_state_change_cb(keynode_t *key, void UNUSED *data)
+{
+}
+
--- /dev/null
+/*
+ * 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 foreach.c
+ * @desc Implementation of the datausage foreach function.
+ *
+ */
+
+
+#include <sqlite3.h>
+#include <string.h>
+
+#include "database.h"
+#include "data_usage.h"
+#include "datausage-foreach.h"
+#include "macro.h"
+#include "trace.h"
+
+#define DATA_USAGE_FOR_PERIOD "select binpath, hw_net_protocol_type, " \
+ "is_roaming, sum(received) as received, " \
+ "sum(sent) as sent, imsi, ground from statistics " \
+ "where time_stamp between ? and ? " \
+ "group by ground, 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, imsi, ground from statistics " \
+ "where time_stamp between ? and ? " \
+ "and iftype=? group by ground, 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, imsi, "\
+ "ground " \
+ "from statistics where time_stamp between ? and ? " \
+ "group by ground, 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, imsi, ground, " \
+ "time_stamp - time_stamp % ? as time_stamp " \
+ "from statistics where time_stamp between ? and ? and iftype=?" \
+ "group by ground, 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, 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, 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, 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, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \
+ "from statistics where time_stamp between ? and ? and binpath = ? " \
+ "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, 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, 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, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \
+ "from statistics where time_stamp between ? and ? " \
+ "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, imsi, ground, time_stamp - time_stamp % ? as time_stamp " \
+ "from statistics where time_stamp between ? and ? " \
+ "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;
+static sqlite3_stmt *data_usage_chunks;
+static sqlite3_stmt *data_usage_chunks_iface;
+static sqlite3_stmt *data_usage_app_details;
+static sqlite3_stmt *data_usage_app_details_iface;
+static sqlite3_stmt *data_usage_chunks_app;
+static sqlite3_stmt *data_usage_chunks_app_iface;
+static sqlite3_stmt *data_usage_total;
+static sqlite3_stmt *data_usage_total_iface;
+static sqlite3_stmt *data_usage_chunks_total;
+static sqlite3_stmt *data_usage_chunks_total_iface;
+
+#define PREPARE(stm, query) do { \
+ rc = sqlite3_prepare_v2(db, query, -1, &stm, NULL); \
+ if (rc != SQLITE_OK) { \
+ stm = NULL; \
+ finalize_datausage_foreach(); \
+ _E("Failed to prepare %s\n", query); \
+ return rc; \
+ } \
+} while (0)
+
+int init_datausage_foreach(sqlite3 *db)
+{
+ int rc;
+ static int initialized;
+
+ if (initialized)
+ return SQLITE_OK;
+
+ PREPARE(data_usage_for_period, DATA_USAGE_FOR_PERIOD);
+ PREPARE(data_usage_for_period_iface, DATA_USAGE_FOR_PERIOD_IFACE);
+ PREPARE(data_usage_chunks, DATA_USAGE_CHUNKS);
+ PREPARE(data_usage_chunks_iface, DATA_USAGE_CHUNKS_IFACE);
+ PREPARE(data_usage_app_details, DATA_USAGE_APP_DETAILS);
+ PREPARE(data_usage_app_details_iface, DATA_USAGE_APP_DETAILS_IFACE);
+ PREPARE(data_usage_chunks_app, DATA_USAGE_CHUNKS_APP);
+ PREPARE(data_usage_chunks_app_iface, DATA_USAGE_CHUNKS_APP_IFACE);
+ PREPARE(data_usage_total, DATA_USAGE_TOTAL);
+ PREPARE(data_usage_total_iface, DATA_USAGE_TOTAL_IFACE);
+ PREPARE(data_usage_chunks_total, DATA_USAGE_CHUNKS_TOTAL);
+ PREPARE(data_usage_chunks_total_iface, DATA_USAGE_CHUNKS_TOTAL_IFACE);
+
+ initialized = 1;
+ return SQLITE_OK;
+}
+
+#define FINALIZE(stm) do { \
+ if (stm) { \
+ sqlite3_finalize(stm); \
+ stm = NULL; \
+ } \
+} while (0)
+
+void finalize_datausage_foreach(void)
+{
+ FINALIZE(data_usage_for_period);
+ FINALIZE(data_usage_for_period_iface);
+ FINALIZE(data_usage_chunks);
+ FINALIZE(data_usage_chunks_iface);
+ FINALIZE(data_usage_app_details);
+ FINALIZE(data_usage_app_details_iface);
+ FINALIZE(data_usage_chunks_app);
+ FINALIZE(data_usage_chunks_app_iface);
+ FINALIZE(data_usage_total);
+ FINALIZE(data_usage_total_iface);
+ FINALIZE(data_usage_chunks_total);
+ FINALIZE(data_usage_chunks_total_iface);
+}
+
+static int is_iftype_defined(const resourced_iface_type iftype)
+{
+ return iftype < RESOURCED_IFACE_LAST_ELEM &&
+ iftype > RESOURCED_IFACE_UNKNOWN &&
+ iftype != RESOURCED_IFACE_ALL;
+}
+
+API resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb,
+ void *user_data)
+{
+ data_usage_info data;
+ sqlite3_stmt *stm;
+ resourced_ret_c result = RESOURCED_ERROR_NONE;
+ int rc;
+ int pos = 1;/* running through positions where to
+ bind parameters in the query */
+ resourced_tm_interval interval;
+
+ libresourced_db_initialize_once();
+ if (init_datausage_foreach(resourced_get_database())!= SQLITE_OK) {
+ _D("Failed to initialize data usage statements: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ if (!rule || !info_cb)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ /* pick a statement depending on parameters */
+ if (rule->granularity) {
+ stm = is_iftype_defined(rule->iftype) ?
+ data_usage_chunks_iface : data_usage_chunks;
+
+ if (sqlite3_bind_int64(stm, pos++, rule->granularity) !=
+ SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ data.interval = &interval;
+ } else {
+ stm = is_iftype_defined(rule->iftype)
+ ? data_usage_for_period_iface : data_usage_for_period;
+ }
+
+ if (sqlite3_bind_int64(stm, pos++, rule->from) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ if (sqlite3_bind_int64(stm, pos++, rule->to) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (is_iftype_defined(rule->iftype)) {
+ data.iftype = rule->iftype;
+ if (sqlite3_bind_int
+ (stm, pos++, rule->iftype) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ }
+
+ do {
+ rc = sqlite3_step(stm);
+ switch (rc) {
+ case SQLITE_ROW:
+ 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);
+ 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, 7);
+ interval.to = interval.from + rule->granularity;
+ }
+
+ if (info_cb(&data, user_data) == RESOURCED_CANCEL)
+ rc = SQLITE_DONE;/* emulate end of data */
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ default:
+ result = RESOURCED_ERROR_DB_FAILED;
+ break;
+ }
+ } while (rc == SQLITE_ROW);
+ out:
+ sqlite3_reset(stm);
+ return result;
+}
+
+/* the following array is strictly ordered
+ * to find required statement the following code will be used:
+ * (iface ? 1 : 0) | (total ? 2 : 0) | (chunks ? 4 : 0)
+ */
+static sqlite3_stmt **details_stms[] = {
+ &data_usage_app_details,
+ &data_usage_app_details_iface,
+ &data_usage_total,
+ &data_usage_total_iface,
+ &data_usage_chunks_app,
+ &data_usage_chunks_app_iface,
+ &data_usage_chunks_total,
+ &data_usage_chunks_total_iface
+};
+
+static sqlite3_stmt *select_statement(const char *app_id,
+ const data_usage_selection_rule *rule)
+{
+ 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,
+ data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb, void *user_data)
+{
+ data_usage_info data;
+ sqlite3_stmt *stm;
+ resourced_ret_c result = RESOURCED_ERROR_NONE;
+ int rc;
+ int pos = 1;/* running through positions
+ where to bind parameters in the query */
+ resourced_tm_interval interval;
+
+ libresourced_db_initialize_once();
+ if (init_datausage_foreach(resourced_get_database())!= SQLITE_OK) {
+ _D("Failed to initialize data usage statements: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ memset(&data, 0, sizeof(data));
+
+ if (!rule || !info_cb)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ /* pick a statement depending on parameters.
+ See comment for details_stms */
+ stm = select_statement(app_id, rule);
+
+ if (rule->granularity) {
+ if (sqlite3_bind_int64(stm, pos++, rule->granularity) !=
+ SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ data.interval = &interval;
+ }
+
+ if (sqlite3_bind_int64(stm, pos++, rule->from) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ if (sqlite3_bind_int64(stm, pos++, rule->to) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (app_id) {
+ if (sqlite3_bind_text(stm, pos++, app_id, -1, SQLITE_TRANSIENT)
+ != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ data.app_id = app_id;
+ }
+
+ if (is_iftype_defined(rule->iftype)) {
+ if (sqlite3_bind_int
+ (stm, pos++, rule->iftype) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ }
+
+ do {
+ rc = sqlite3_step(stm);
+ switch (rc) {
+ case SQLITE_ROW:
+ data.iftype = sqlite3_column_int(stm, 0);
+ data.hw_net_protocol_type = sqlite3_column_int(stm, 1);
+ data.roaming = sqlite3_column_int(stm, 2);
+ 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, 7);
+ interval.to = interval.from + rule->granularity;
+ }
+
+ if (info_cb(&data, user_data) == RESOURCED_CANCEL)
+ rc = SQLITE_DONE; /* emulate end of data */
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ default:
+ result = RESOURCED_ERROR_DB_FAILED;
+ break;
+ }
+ } while (rc == SQLITE_ROW);
+ out:
+ sqlite3_reset(stm);
+ return result;
+}
--- /dev/null
+/*
+ * 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 generic-netlink.c
+ *
+ * @desc User space code for ktgrabber logic
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <errno.h>
+#include <data_usage.h>
+#include <glib.h>
+#include <sys/socket.h> /*for netlink.h*/
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <Ecore.h>
+
+#include "net-cls-cgroup.h"
+#include "const.h"
+#include "generic-netlink.h"
+#include "genl.h"
+#include "iface.h"
+#include "macro.h"
+#include "trace.h"
+#include "transmission.h"
+
+#define NESTED_MCAST_MAX 256
+#define MAX_PAYLOAD 1024 /* maximum payload size */
+
+uint32_t netlink_get_family(struct genl *nl_ans)
+{
+ return nl_ans->n.nlmsg_type;
+}
+
+/*
+ * Send netlink message to kernel
+ */
+static int send_message(int fd, const char *message, int msg_len)
+{
+ struct sockaddr_nl nl_addr;
+ int ret = 0;
+
+ memset(&nl_addr, 0, sizeof(nl_addr));
+ nl_addr.nl_family = AF_NETLINK;
+
+ while ((ret =
+ sendto(fd, message, msg_len, 0, (struct sockaddr *)&nl_addr,
+ sizeof(nl_addr))) < msg_len) {
+ if (ret <= 0 && errno != EAGAIN)
+ return ret;
+ else if (errno == EAGAIN)
+ continue;
+
+ message += ret;
+ msg_len -= ret;
+ }
+ return 0;
+}
+
+/*
+ * Probe the controller in genetlink to find the family id
+ * for the TRAF_STAT family
+ */
+
+uint32_t get_family_id(int sock, pid_t pid,
+ char *family_name)
+{
+ uint32_t family_id = 0;
+ uint32_t UNUSED group_id = get_family_group_id(sock, pid, family_name, NULL,
+ &family_id);
+ return family_id;
+}
+
+static int extract_group_id(const struct rtattr *rt_na, const char *group_name,
+ uint32_t *group_id)
+{
+ struct rtattr *multicast_group_family[__CTRL_ATTR_MCAST_GRP_MAX] = {0};
+ char *name;
+ struct rtattr *rt_nested;
+ int rt_len;
+
+ if (!rt_na)
+ return -EINVAL;
+
+ rt_nested = RTA_DATA(rt_na); /* nested */
+ rt_len = RTA_PAYLOAD(rt_na);
+
+ fill_attribute_list(multicast_group_family,
+ CTRL_ATTR_MCAST_GRP_MAX, rt_nested, rt_len);
+
+ if (!multicast_group_family[CTRL_ATTR_MCAST_GRP_NAME] ||
+ !multicast_group_family[CTRL_ATTR_MCAST_GRP_ID])
+ return -EINVAL;
+
+ name = RTA_DATA(multicast_group_family[CTRL_ATTR_MCAST_GRP_NAME]);
+
+ if (strcmp(name, group_name))
+ return -EINVAL;
+
+ *group_id = *((__u32 *)RTA_DATA(
+ multicast_group_family[CTRL_ATTR_MCAST_GRP_ID]));
+ return RESOURCED_ERROR_NONE;
+}
+
+
+/*
+ * check subattribute CTRL_ATTR_MCAST_GROUPS
+ * if it exists we are dealing with broadcast generic
+ * netlink message
+ * message format is following
+ * CTRL_ATTR_MCAST_GROUPS
+ * ATTR1
+ * CTRL_ATTR_MCAST_GRP_NAME
+ * CTRL_ATTR_MCAST_GRP_ID
+ * ATTR2
+ * CTRL_ATTR_MCAST_GRP_NAME
+ * CTRL_ATTR_MCAST_GRP_ID
+ * ...
+ */
+static uint32_t get_mcast_group_id(struct rtattr *mc_na, const char *group_name)
+{
+ struct rtattr *rt_na = RTA_DATA(mc_na); /* nested */
+ int rt_len = RTA_PAYLOAD(mc_na);
+ int i, ret;
+ uint32_t group_id;
+
+ struct rtattr *multicast_general_family[NESTED_MCAST_MAX + 1] = {0};
+
+ fill_attribute_list(multicast_general_family, NESTED_MCAST_MAX,
+ rt_na, rt_len);
+
+ /* for each group */
+ for (i = 0; i < NESTED_MCAST_MAX; ++i) {
+ /* if this group is valid */
+ if (!multicast_general_family[i])
+ continue;
+
+ ret = extract_group_id(multicast_general_family[i], group_name,
+ &group_id);
+ if (ret == RESOURCED_ERROR_NONE)
+ return group_id;
+ }
+
+ return 0;
+}
+
+uint32_t get_family_group_id(int sock, pid_t pid,
+ char *family_name, char *group_name,
+ uint32_t *family_id)
+{
+ struct genl family_req;
+ struct genl ans;
+
+ struct nlattr *na = 0;
+ int rep_len = 0, ret;
+ struct rtattr *general_family[__CTRL_ATTR_MAX] = {0};
+ struct rtattr *rt_na;
+ static uint32_t seq;
+
+ ret_value_msg_if(sock < 0, 0, "Please provide valid socket!");
+
+ family_req.n.nlmsg_type = GENL_ID_CTRL;
+ family_req.n.nlmsg_flags = NLM_F_REQUEST;
+ family_req.n.nlmsg_seq = seq++;
+ family_req.n.nlmsg_pid = pid;
+ family_req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ family_req.g.cmd = CTRL_CMD_GETFAMILY;
+
+ na = (struct nlattr *)GENLMSG_DATA(&family_req);
+ na->nla_type = CTRL_ATTR_FAMILY_NAME;
+
+ na->nla_len = strlen(family_name) + 1 + NLA_HDRLEN;
+ strcpy(NLA_DATA(na), family_name);
+
+ family_req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
+
+ ret = send_message(sock, (char *)&family_req, family_req.n.nlmsg_len);
+
+ ret_value_msg_if(ret < 0, 0, "Failed to send GETFAMILY command");
+
+ rep_len = recv(sock, &ans, sizeof(ans), 0);
+
+ ret_value_msg_if(rep_len < 0, 0,
+ "Failed to receive answer for GETFAMILY command");
+
+ /* Validate response message */
+ if (!NLMSG_OK((&ans.n), rep_len)) {
+ _E("Invalid reply message\n");
+ return 0;
+ }
+
+ ret_value_msg_if(ans.n.nlmsg_type == NLMSG_ERROR, 0,
+ "Invalid netlink message format");
+
+ rt_na = (struct rtattr *)GENLMSG_DATA(&ans);
+
+ fill_attribute_list(general_family, CTRL_ATTR_MAX, rt_na, rep_len);
+
+ /* family id for netlink is 16 bits long for multicast is 32 bit */
+ if (general_family[CTRL_ATTR_FAMILY_ID])
+ *family_id = *(__u16 *)RTA_DATA(
+ general_family[CTRL_ATTR_FAMILY_ID]);
+
+ /* group name wasn't requested */
+ if (!group_name)
+ return 0;
+
+ if (!general_family[CTRL_ATTR_MCAST_GROUPS])
+ return 0;
+
+ return get_mcast_group_id(general_family[CTRL_ATTR_MCAST_GROUPS],
+ group_name);
+}
+
+#ifdef NETWORK_DEBUG_ENABLED
+static void show_result(const struct genl *ans)
+{
+ /*parse reply message */
+ struct nlattr *na = NULL;
+ char *result = NULL;
+
+ if (!ans) {
+ _D ("Please provide valid argument!");
+ return;
+ }
+
+ na = (struct nlattr *)GENLMSG_DATA(ans);
+ result = (char *)NLA_DATA(na);
+ if (result)
+ _D("Initialization result: %s\n", result);
+ else
+ _D("Failed to show initialization result!");
+}
+#else /* Release build */
+static void show_result(const struct genl *ans)
+{
+}
+#endif
+
+static resourced_ret_c send_common_cmd(int sock, const pid_t pid,
+ const uint32_t family_id, const __u8 cmd)
+{
+ struct genl ans;
+ int r;
+
+ ret_value_msg_if(sock < 0, RESOURCED_ERROR_NONE,
+ "Please provide valid socket!");
+
+ r = send_command(sock, pid, family_id, cmd);
+
+ ret_value_errno_msg_if(r < 0, RESOURCED_ERROR_FAIL,
+ "Failed to send command");
+
+ /* Read message from kernel */
+ r = recv(sock, &ans, sizeof(ans), MSG_DONTWAIT);
+
+ ret_value_errno_msg_if(r < 0, RESOURCED_ERROR_FAIL,
+ "Cant receive message from kernel");
+
+ ret_value_msg_if(ans.n.nlmsg_type == NLMSG_ERROR, RESOURCED_ERROR_FAIL,
+ "Netlink format error");
+
+ ret_value_msg_if(!NLMSG_OK((&ans.n), r), RESOURCED_ERROR_FAIL,
+ "Invalid reply message received via Netlink");
+
+ show_result(&ans);
+ return RESOURCED_ERROR_NONE;
+}
+
+static resourced_ret_c run_net_activity(const __u8 cmd)
+{
+ int sock;
+ uint32_t family_id;
+ resourced_ret_c ret;
+ pid_t pid;
+ sock = create_netlink(NETLINK_GENERIC, 0);
+
+ ret_value_msg_if(sock < 0, RESOURCED_ERROR_FAIL,
+ "Failed to create netlink socket");
+ pid = getpid();
+ family_id = get_family_id(sock, pid, "NET_ACTIVITY");
+ if (!family_id) {
+ _E("Invalid family id number");
+ close(sock);
+ return RESOURCED_ERROR_FAIL;
+ }
+ /* send without handling response */
+ ret = send_command(sock, pid, family_id, cmd);
+
+ if (ret != RESOURCED_ERROR_NONE) {
+ ETRACE_ERRNO_MSG("Failed to send \
+ net_activity command %u", cmd);
+ /* send_command return errno */
+ ret = RESOURCED_ERROR_FAIL;
+ }
+
+ close(sock);
+
+ return ret;
+}
+
+resourced_ret_c start_net_activity(void)
+{
+ return run_net_activity(NET_ACTIVITY_C_START);
+}
+
+resourced_ret_c stop_net_activity(void)
+{
+ return run_net_activity(NET_ACTIVITY_C_STOP);
+}
+
+
+void send_start(int sock, const pid_t pid, const int family_id)
+{
+ send_common_cmd(sock, pid, family_id, TRAF_STAT_C_START);
+}
+
+int send_command(int sock, const pid_t pid, const int family_id, __u8 cmd)
+{
+ struct genl req;
+ struct nlattr *na;
+ struct sockaddr_nl nladdr;
+ const char *message = "INIT";
+ const int mlength = sizeof(message) + 1;
+
+ ret_value_msg_if(sock < 0, RESOURCED_ERROR_NONE,
+ "Please provide valid socket!");
+
+ /* Send command needed */
+ req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req.n.nlmsg_type = family_id;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_seq = 60;
+ req.n.nlmsg_pid = pid;
+ req.g.cmd = cmd;
+
+ /* compose message */
+ na = (struct nlattr *)GENLMSG_DATA(&req);
+ na->nla_type = 1;
+ na->nla_len = mlength + NLA_HDRLEN; /* message length */
+ memcpy(NLA_DATA(na), message, mlength);
+ req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
+
+ /* send message */
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(sock, (char *)&req, req.n.nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+}
+
+int send_restriction(int sock, const pid_t pid, const int family_id,
+ const u_int32_t classid, const int ifindex,
+ const enum traffic_restriction_type restriction_type,
+ const int send_limit, const int rcv_limit,
+ const int snd_warning_threshold, const int rcv_warning_threshold)
+{
+ struct genl req;
+ struct traffic_restriction rst = {
+ .sk_classid = classid,
+ .type = restriction_type,
+ .ifindex = ifindex,
+ .send_limit = send_limit,
+ .rcv_limit = rcv_limit,
+ .snd_warning_threshold = snd_warning_threshold,
+ .rcv_warning_threshold = rcv_warning_threshold,
+ };
+
+ struct nlattr *na;
+ struct sockaddr_nl nladdr;
+ int mlength = 0, r = 0;
+
+ if (sock < 0) {
+ _D("Can't use socket\n");
+ return -1;
+ }
+
+ /* Send command needed */
+ req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req.n.nlmsg_type = family_id;
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_seq = 60;
+ req.n.nlmsg_pid = pid;
+ req.g.cmd = TRAF_STAT_C_SET_RESTRICTIONS;
+
+ /*compose message */
+ na = (struct nlattr *)GENLMSG_DATA(&req);
+ na->nla_type = TRAF_STAT_DATA_RESTRICTION;
+ mlength = sizeof(struct traffic_restriction); /* * classid_count; */
+ na->nla_len = mlength + NLA_HDRLEN;
+
+ memcpy(NLA_DATA(na), &rst, sizeof(struct traffic_restriction));
+ req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
+
+ /*send message */
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ /*use send_message */
+ r = sendto(sock, (char *)&req, req.n.nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ _D("Restriction send to kernel, result: %d", r);
+ return r;
+}
+
+resourced_ret_c process_netlink_restriction_msg(const struct genl *ans,
+ struct traffic_restriction *restriction, uint8_t *command)
+{
+ struct rtattr *na;
+ struct rtattr *attr_list[__RESTRICTION_NOTI_A_MAX] = {0};
+
+ int len = GENLMSG_PAYLOAD(&ans->n);
+
+ if (!restriction || !command)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (len <= 0)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ *command = ans->g.cmd;
+
+ /* parse reply message */
+ na = (struct rtattr *)GENLMSG_DATA(ans);
+
+ fill_attribute_list(attr_list, __RESTRICTION_NOTI_A_MAX - 1,
+ na, len);
+
+ ret_value_msg_if(!attr_list[RESTRICTION_A_CLASSID], RESOURCED_ERROR_FAIL,
+ "Restriction netlink message doesn't contain mandatory classid.");
+
+ restriction->sk_classid = *(uint32_t *)RTA_DATA(
+ attr_list[RESTRICTION_A_CLASSID]);
+
+ if (attr_list[RESTRICTION_A_IFINDEX])
+ restriction->ifindex = *(int *)RTA_DATA(
+ attr_list[RESTRICTION_A_IFINDEX]);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+enum net_activity_recv recv_net_activity(int sock, struct net_activity_info
+ *activity_info, const uint32_t net_activity_family_id)
+{
+ int ans_len, traffic_type;
+ struct traffic_event *event;
+ struct nlattr *na = 0;
+ struct genl ans;
+ uint32_t family_id;
+
+ ans_len = recv(sock, &ans, sizeof(ans),
+ MSG_DONTWAIT);
+
+ if (ans_len <= 0 || !NLMSG_OK((&ans.n), ans_len)) {
+ ETRACE_ERRNO_MSG("Failed to read netlink socket %d",
+ ans_len);
+ return RESOURCED_NET_ACTIVITY_STOP;
+ }
+
+ _D("Reading multicast netlink message len %d", ans_len);
+
+ family_id = netlink_get_family(&ans);
+
+ if (family_id != net_activity_family_id) {
+ _D("Received family_id %d", family_id);
+ return RESOURCED_NET_ACTIVITY_CONTINUE;
+ }
+
+ na = (struct nlattr *)GENLMSG_DATA(&ans);
+
+ traffic_type = na->nla_type;
+
+ event = (struct traffic_event *) NLA_DATA(na);
+ activity_info->type = traffic_type;
+ activity_info->bytes = event->bytes;
+ activity_info->iftype = get_iftype(event->ifindex);
+ activity_info->appid = get_app_id_by_classid(event->sk_classid, true);
+
+ return RESOURCED_NET_ACTIVITY_OK;
+}
--- /dev/null
+/*
+ * 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 iface-cb.c
+ *
+ * @desc Network interface callbacks entity
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "macro.h"
+#include "datausage-common.h"
+#include "restriction-handler.h"
+#include "settings.h"
+#include "storage.h"
+#include "trace.h"
+
+#include <Ecore.h>
+#include <linux/genetlink.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h> /*for netlink.h*/
+#include <sys/types.h>
+#include <unistd.h>
+
+#define ADDR_EVENT_BUF_LEN 4096
+static int iface_fd;
+
+static iface_callbacks *ifcallbacks;
+static Ecore_Fd_Handler *iface_ecore_fd_handler;
+
+static iface_callbacks *create_iface_callback(void)
+{
+ iface_callbacks *callbacks = NULL;
+ gpointer callback_data = create_restriction_callback();
+
+ if (callback_data)
+ callbacks = g_list_prepend(callbacks, callback_data);
+ callback_data = create_iface_storage_callback();
+ if (callback_data)
+ callbacks = g_list_prepend(callbacks, callback_data);
+ callback_data = create_counter_callback();
+ if (callback_data)
+ callbacks = g_list_prepend(callbacks, callback_data);
+
+ return callbacks;
+}
+
+static void _iface_up_iter(gpointer data, gpointer user_data)
+{
+ iface_callback *arg = (iface_callback *)data;
+ uint32_t ifindex = *(uint32_t *) (user_data);
+ if (arg && arg->handle_iface_up)
+ arg->handle_iface_up(ifindex);
+}
+
+static void _iface_down_iter(gpointer data, gpointer user_data)
+{
+ iface_callback *arg = (iface_callback *)data;
+ uint32_t ifindex = *(uint32_t *)(user_data);
+ if (arg && arg->handle_iface_down)
+ arg->handle_iface_down(ifindex);
+}
+
+static void process_nlh(int len, const struct nlmsghdr *nlh,
+ iface_callbacks *arg)
+{
+ if (!arg) {
+ _D("Please provide valid argument!");
+ return;
+ }
+
+ for (; (NLMSG_OK(nlh, len)) &&
+ (nlh->nlmsg_type != NLMSG_DONE);
+ nlh = NLMSG_NEXT(nlh, len)) {
+ if (nlh->nlmsg_type != RTM_NEWADDR
+ && nlh->nlmsg_type != RTM_DELADDR)
+ continue;
+
+ struct ifaddrmsg *ifa =
+ (struct ifaddrmsg *) NLMSG_DATA(nlh);
+ struct rtattr *rth = IFA_RTA(ifa);
+ int rtl = IFA_PAYLOAD(nlh);
+
+ for (; rtl && RTA_OK(rth, rtl);
+ rth = RTA_NEXT(rth, rtl)) {
+ if (rth->rta_type != IFA_LOCAL)
+ continue;
+
+ if (nlh->nlmsg_type == RTM_NEWADDR) {
+ init_iftype();
+ return g_list_foreach(arg, _iface_up_iter,
+ &(ifa->ifa_index));
+
+ } else if (nlh->nlmsg_type == RTM_DELADDR) {
+ g_list_foreach(arg, _iface_down_iter,
+ &(ifa->ifa_index));
+ /* network delete hooks require old information,
+ * for example for get_iftype by ifindex */
+ init_iftype();
+ return;
+ }
+ }
+ }
+}
+
+static Eina_Bool iface_func_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
+{
+ char buff[ADDR_EVENT_BUF_LEN];
+ struct nlmsghdr *nlh;
+ iface_callbacks *localiarg = (iface_callbacks *)user_data;
+ int fd;
+ int len;
+
+ 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_RENEW;
+ }
+
+ fd = ecore_main_fd_handler_fd_get(fd_handler);
+ if (fd < 0) {
+ _E("ecore_main_fd_handler_fd_get error");
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ len = read(fd, buff, ADDR_EVENT_BUF_LEN);
+ if (len < 0) {
+ _E("socket read error");
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ nlh = (struct nlmsghdr *)buff;
+ process_nlh(len, nlh, localiarg);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static int iface_func_init()
+{
+ struct sockaddr_nl addr = {0};
+ int sock, error = 0, on;
+
+ sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock == -1) {
+ _E("Error creating NETLINK_ROUTE socket");
+ error = errno;
+ goto handle_error;
+ }
+
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR;
+/* Enable address reuse */
+ on = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+ _E("Error setsockopt");
+ error = errno;
+ goto release_socket;
+ }
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ _E("Error bind socket");
+ error = errno;
+ goto release_socket;
+ }
+
+ _D("Socket created successfully\n");
+
+ return sock;
+
+release_socket:
+ close(sock);
+handle_error:
+ return -error;
+}
+
+int resourced_iface_init(void)
+{
+ ifcallbacks = create_iface_callback();
+ _D("Initialize network interface callbacks\n");
+ ret_value_msg_if(ifcallbacks == NULL, RESOURCED_ERROR_FAIL,
+ "Error create network interface callbacks");
+ iface_fd = iface_func_init();
+ ret_value_msg_if(iface_fd < 0, RESOURCED_ERROR_FAIL,
+ "Can not listen network interface changes %d",
+ iface_fd);
+ iface_ecore_fd_handler = ecore_main_fd_handler_add(
+ iface_fd, ECORE_FD_READ, iface_func_cb,
+ (void *)ifcallbacks, NULL, NULL);
+ ret_value_msg_if(iface_ecore_fd_handler == NULL, RESOURCED_ERROR_FAIL,
+ "Failed to add iface callbacks\n");
+ return RESOURCED_ERROR_NONE;
+}
+
+void resourced_iface_finalize(void)
+{
+ _D("Finalize network interface callbacks\n");
+ ecore_main_fd_handler_del(iface_ecore_fd_handler);
+ shutdown(iface_fd, 2);
+ close(iface_fd);
+ g_list_free_full(ifcallbacks, free);
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2014 - 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 iface.c
+ *
+ * @desc Utility for working with network interfaces
+ */
+
+
+#include <errno.h>
+#include <glib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <stdbool.h>
+#include <linux/un.h>
+#include <net/if.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "const.h"
+#include "iface.h"
+#include "macro.h"
+#include "trace.h"
+
+#define IFACES_TYPE_SECTION "IFACES_TYPE"
+
+static int iface_stat[RESOURCED_IFACE_LAST_ELEM - 1];
+static GTree *iftypes; /* holds int key and value of type resourced_iface_type */
+
+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";
+static const char *WIRED_VALUE = "gadget";
+static const char *WIFI_VALUE = "wlan";
+static const char *BLUETOOTH_VALUE = "bluetooth";
+static const char *DATACALL_VALUE = "datacall";
+static const char *ALL_NET_IFACE_VALUE = "all";
+static const char *UEVENT_DELIM = "=\n";
+
+static GSList *ifnames_relations;
+
+struct iface_relation {
+ resourced_iface_type iftype;
+ char ifname[MAX_NAME_LENGTH];
+};
+
+struct iface_status {
+ bool active;
+ char ifname[MAX_NAME_LENGTH];
+ resourced_iface_type iftype;
+};
+
+static allowance_cb allow_cb;
+
+static gint compare_int(gconstpointer a, gconstpointer b,
+ gpointer UNUSED userdata)
+{
+ if (a == b)
+ return 0;
+ else if (a > b)
+ return 1;
+ return -1;
+}
+
+static GTree *create_iface_tree(void)
+{
+ return g_tree_new_full(compare_int,
+ NULL, NULL, free);
+}
+
+static void put_iftype_to_tree(GTree *iftypes_tree, int ifindex, int iftype)
+{
+ gpointer new_value;
+
+ ret_msg_if(!iftypes_tree, "Please provide valid argument!");
+ new_value = (gpointer)malloc(sizeof(int));
+ if (!new_value) {
+ _E("Malloc of put_iftype_to_tree failed\n");
+ return;
+ }
+ *(int *)new_value = iftype;
+ g_tree_replace(iftypes_tree, (gpointer)ifindex, new_value);
+}
+
+static void keep_ifname(GSList **ifnames_list, char *ifname, int iftype)
+{
+ 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;
+ }
+ }
+
+ 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;
+ }
+}
+
+static resourced_iface_type get_iftype_from_tree(GTree *iftypes_tree, int ifindex)
+{
+ resourced_iface_type ret = RESOURCED_IFACE_UNKNOWN;
+ gpointer table_value;
+
+ ret_value_msg_if(!iftypes_tree, ret, "Please provide valid argument!");
+
+ pthread_rwlock_rdlock(&iftypes_guard);
+ table_value = g_tree_lookup(iftypes_tree, (gpointer)ifindex);
+ pthread_rwlock_unlock(&iftypes_guard);
+ if (table_value != NULL)
+ ret = *(int *)table_value;
+
+ return ret;
+}
+
+static void free_iftypes_tree(GTree *iftypes_tree)
+{
+ g_tree_destroy(iftypes_tree);
+}
+
+static void iface_stat_allowance(void)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(iface_stat); ++i)
+ iface_stat[i] = 1;
+}
+
+static resourced_iface_type get_predefined_iftype(const char *ifname)
+{
+ struct iface_relation *relation;
+ GSList *iter;
+ gslist_for_each_item(iter, ifnames_relations) {
+ relation = (struct iface_relation *)iter->data;
+ if (strstr(ifname, relation->ifname))
+ return relation->iftype;
+ }
+ _D("Even in predefined interface name list, interface types wasn't "
+ " find for %s", ifname);
+ return RESOURCED_IFACE_UNKNOWN;
+}
+
+static resourced_iface_type read_iftype(const char *iface)
+{
+ char buffer[UNIX_PATH_MAX];
+ char *key_buffer;
+ char *value_buffer;
+ char *saveptr;
+ FILE *uevent;
+ resourced_iface_type ret = RESOURCED_IFACE_UNKNOWN;
+
+ snprintf(buffer, UNIX_PATH_MAX, UEVENT_FMT, iface);
+ uevent = fopen(buffer, "r");
+
+ if (!uevent)
+ return ret;
+
+ while (!feof(uevent)) {
+ if (fgets(buffer, UNIX_PATH_MAX, uevent) == NULL)
+ break;
+ key_buffer = strtok_r(buffer, UEVENT_DELIM, &saveptr);
+ value_buffer = strtok_r(NULL, UEVENT_DELIM, &saveptr);
+ if (key_buffer && strcmp(key_buffer, DEVTYPE_KEY) != 0)
+ continue;
+ ret = convert_iftype(value_buffer);
+ break;
+ }
+
+ fclose(uevent);
+
+ /* work around, in case of missing DEVTYPE field */
+ if (ret == RESOURCED_IFACE_UNKNOWN)
+ ret = get_predefined_iftype(iface);
+
+ return ret;
+}
+
+static void reset_tree(GTree *new, GTree **old,
+ pthread_rwlock_t *guard)
+{
+ GTree *release = *old;
+
+ pthread_rwlock_wrlock(guard);
+ *old = new;
+ pthread_rwlock_unlock(guard);
+ if (release)
+ free_iftypes_tree(release);
+}
+
+bool is_address_exists(const char *name)
+{
+#ifdef SIOCDIFADDR
+ struct ifreq ifr;
+ static int fd;
+ if (!fd)
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)-1);
+ return ioctl(fd, SIOCGIFADDR, &ifr) == 0;
+#endif /* SIOCDIFADDR */
+ return true;
+}
+
+int fill_ifaces_relation(struct parse_result *result,
+ void UNUSED *user_data)
+{
+ struct iface_relation *relation;
+ if (strcmp(result->section, IFACES_TYPE_SECTION))
+ return RESOURCED_ERROR_NONE;
+
+ relation = (struct iface_relation *)malloc(sizeof(struct iface_relation));
+
+ ret_value_msg_if(relation == NULL, RESOURCED_ERROR_NONE,
+ "Failed to allocated memory!");
+
+ relation->iftype = convert_iftype(result->name);
+ STRING_SAVE_COPY(relation->ifname, result->value);
+
+ ifnames_relations = g_slist_prepend(ifnames_relations, relation);
+ return RESOURCED_ERROR_NONE;
+}
+
+int init_iftype(void)
+{
+ int i;
+ resourced_iface_type iftype;
+ struct if_nameindex *ids = if_nameindex();
+ GTree *iftypes_next = create_iface_tree();
+
+ if (ids == NULL) {
+ _E("Failed to initialize iftype table! errno: %d, %s",
+ errno, strerror_r(errno, buf, sizeof(buf)));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!ifnames_relations) {
+ _D("interface name relations are empty");
+ }
+
+ 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 */
+ keep_ifname(&ifnames, ids[i].if_name, iftype);
+ _D("ifname %s, ifype %d", ids[i].if_name, iftype);
+ }
+
+ /* Do not forget to free the memory */
+ if_freenameindex(ids);
+
+ reset_tree(iftypes_next, &iftypes, &iftypes_guard);
+ return RESOURCED_ERROR_NONE;
+}
+
+void finalize_iftypes(void)
+{
+ reset_tree(NULL, &iftypes, &iftypes_guard);
+ g_slist_free_full(ifnames, free);
+ g_slist_free_full(ifnames_relations, free);
+}
+
+resourced_iface_type convert_iftype(const char *buffer)
+{
+ if (!buffer) {
+ _E("Malloc of answer_get_stat failed\n");
+ return RESOURCED_IFACE_UNKNOWN;
+ }
+
+ if (strcmp(buffer, DATACALL_VALUE) == 0)
+ return RESOURCED_IFACE_DATACALL;
+
+ if (strcmp(buffer, WIFI_VALUE) == 0)
+ return RESOURCED_IFACE_WIFI;
+
+ if (strcmp(buffer, BLUETOOTH_VALUE) == 0)
+ return RESOURCED_IFACE_BLUETOOTH;
+
+ if (strcmp(buffer, WIRED_VALUE) == 0)
+ return RESOURCED_IFACE_WIRED;
+ if (strcmp(buffer, ALL_NET_IFACE_VALUE) == 0)
+ return RESOURCED_IFACE_ALL;
+ return RESOURCED_IFACE_UNKNOWN;
+}
+
+int is_counting_allowed(resourced_iface_type iftype)
+{
+ return iface_stat[iftype];
+}
+
+API resourced_iface_type get_iftype(int ifindex)
+{
+ return get_iftype_from_tree(iftypes, ifindex);
+}
+
+static char *lookup_ifname(GSList *ifnames_list, int iftype)
+{
+ GSList *iter;
+
+ ret_value_msg_if(!ifnames_list, NULL, "Please provide valid argument!");
+
+ gslist_for_each_item(iter, ifnames_list) {
+ struct iface_status *value = (struct iface_status *)iter->data;
+ if (value->iftype == iftype)
+ return value->ifname;
+ }
+
+ return NULL;
+}
+
+char *get_iftype_name(resourced_iface_type iftype)
+{
+ return lookup_ifname(ifnames, iftype);
+}
+
+resourced_iface_type get_iftype_by_name(char *name)
+{
+ GSList *iter;
+ ret_value_msg_if(name == NULL, RESOURCED_IFACE_UNKNOWN,
+ "Invalid argument");
+
+ gslist_for_each_item(iter, ifnames) {
+ struct iface_status *value = (struct iface_status *)iter->data;
+ if (!strcmp(value->ifname, name))
+ return value->iftype;
+ }
+
+ 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);
+ 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 (!is_counting_allowed(value->iftype) && empty_func) {
+ empty_func(data);
+ continue;
+ }
+
+ if (iter_cb(value->iftype, value->ifname, data) == TRUE)
+ break;
+ }
+
+ if (!g_slist_length(ifnames) && empty_func)
+ empty_func(data);
+
+}
+
+void set_wifi_allowance(const resourced_option_state wifi_option)
+{
+ int old_allowance = iface_stat[RESOURCED_IFACE_WIFI];
+ iface_stat[RESOURCED_IFACE_WIFI] = wifi_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
+
+ if (old_allowance != iface_stat[RESOURCED_IFACE_WIFI] && allow_cb)
+ allow_cb(RESOURCED_IFACE_WIFI, iface_stat[RESOURCED_IFACE_WIFI]);
+}
+
+void set_datacall_allowance(const resourced_option_state datacall_option)
+{
+ int old_allowance = iface_stat[RESOURCED_IFACE_DATACALL];
+
+ iface_stat[RESOURCED_IFACE_DATACALL] = datacall_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
+ if (old_allowance != iface_stat[RESOURCED_IFACE_DATACALL] && allow_cb)
+ allow_cb(RESOURCED_IFACE_DATACALL, iface_stat[RESOURCED_IFACE_DATACALL]);
+}
+
+void set_change_allow_cb(allowance_cb cb)
+{
+ allow_cb = cb;
+}
--- /dev/null
+/*
+ * 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: app-stat.h
+ *
+ * @desc Application stat entity
+ * @version 1.0
+ *
+ */
+
+#ifndef _RESOURCED_APPLICATION_STAT_H_
+#define _RESOURCED_APPLICATION_STAT_H_
+
+#include <netinet/in.h>
+#include <glib.h>
+#include <sys/types.h>
+
+#include "const.h"
+#include "config.h"
+#include "data_usage.h"
+#include "transmission.h"
+
+#define RSML_UNKNOWN_CLASSID 1
+
+/*
+* General structure containing information for storing
+* application_id - package name as unique application identifier
+* snd_count - sent bytes
+* rcv_count - received bytes
+* pid - process identifier
+* ifindex - network interface index, iftype holds in key @see resourced_iface_type
+* is_roaming - is traffic consumed at roaming, @see resourced_roaming_type
+*/
+struct application_stat {
+ char *application_id;
+ uint32_t snd_count;
+ uint32_t rcv_count;
+ uint32_t delta_snd;
+ uint32_t delta_rcv;
+
+#ifndef CONFIG_DATAUSAGE_NFACCT
+ pid_t pid;
+ int ifindex;
+#endif
+ resourced_roaming_type is_roaming;
+
+ /* 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;
+ /* pointer to telephony's imsi */
+ char *imsi;
+ char ifname[MAX_IFACE_LENGTH];
+};
+
+struct application_stat_tree {
+ GTree *tree;
+ time_t last_touch_time;
+ pthread_rwlock_t guard;
+};
+
+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);
+
+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
+
+
+
+#endif /* _RESOURCED_APPLICATION_STAT_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 counter-process.h
+ *
+ * @desc Counter process entity
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_DATAUSAGE_COUNTER_PROCESS_H
+#define _RESOURCED_DATAUSAGE_COUNTER_PROCESS_H
+
+#include "counter.h"
+
+int resourced_init_counter_func(struct counter_arg *carg);
+
+void resourced_finalize_counter_func(struct counter_arg *carg);
+
+#endif /* _RESOURCED_DATAUSAGE_COUNTER_PROCESS_H */
--- /dev/null
+/*
+ * 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: counter.h
+ *
+ * @desc Entity for working with datausage counter.
+ * In plans place to counter.c main counting procedure from main.c
+ */
+
+
+#ifndef _RESOURCED_DATAUSAGE_COUNTER_H
+#define _RESOURCED_DATAUSAGE_COUNTER_H
+
+#include "app-stat.h"
+#include "config.h"
+
+#include <Ecore.h>
+
+#define RESOURCED_BACKGROUND_APP_NAME "BACKGROUND"
+
+struct counter_arg {
+ int sock;
+ int ans_len;
+#ifndef CONFIG_DATAUSAGE_NFACCT
+ pid_t pid;
+ int family_id_stat;
+ int family_id_restriction;
+#else
+ GTree *nf_cntrs;
+ int initiate;
+ int noti_fd;
+ Ecore_Fd_Handler *noti_fd_handler;
+#endif
+ int serialized_counters; /* number of counters which was serialized in
+ current request */
+ struct net_counter_opts *opts;
+ struct application_stat_tree *result;
+ 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;
+};
+
+struct net_counter_opts {
+ sig_atomic_t update_period;
+ sig_atomic_t flush_period;
+ sig_atomic_t state;
+};
+
+/**
+ * @desc Reschedule existing traffic counter function
+ * Rescheduling logic is following, we will postpone
+ * execution on delay seconds.
+ */
+void reschedule_count_timer(const struct counter_arg *carg, const double delay);
+
+struct counter_arg *init_counter_arg(struct net_counter_opts *opts);
+
+void finalize_carg(struct counter_arg *carg);
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+GTree *create_nfacct_tree(void);
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+#endif /* _RESOURCED_NETWORK_COUNTING_H_ */
+
+
--- /dev/null
+/*
+ * 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: database.h
+ *
+ * @desc Performance management API. Database helper
+ * @version 1.0
+ *
+ * Created on: Jun 29, 2012
+ */
+
+#include <sqlite3.h>
+
+#ifndef TRESOURCED_DATABASE_H_
+#define TRESOURCED_DATABASE_H_
+
+sqlite3 *resourced_get_database(void);
+
+/**
+ * @desc Initialize DB and DB statement
+ * only once time
+ */
+void libresourced_db_initialize_once(void);
+
+#endif /* TRESOURCED_DATABASE_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 netstat-common.h
+ *
+ * @desc Datausage module control structures
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef __RESOURCED_NETSTAT_COMMON_H__
+#define __RESOURCED_NETSTAT_COMMON_H__
+
+#include <stdint.h>
+
+#include <resourced.h>
+
+#include "counter.h"
+#include "nfacct-rule.h"
+#include "iface.h"
+#include "proc-common.h"
+
+enum netstat_control_type {
+ NET_CTRL_TYPE_UNKNOWN,
+ JOIN_NET_CLS,
+ NET_CTRL_TYPE_LAST_ELEM,
+};
+
+struct netstat_data_type
+{
+ enum netstat_control_type op_type;
+ uint32_t *args;
+};
+
+/**
+ * @brief It creates an appropriate cgroup,
+ * it generates classid for the network performance control.
+ * This function uses module's control callback interface to
+ * invoke join_app_performance
+ * @param app_id[in] - application identifier, it's package name now
+ * @param pid - pid to put in to cgroup, or self pid of 0
+ * @return 0 if success or error code
+ */
+resourced_ret_c join_net_cls(const char *app_id, const pid_t pid);
+
+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);
+
+/**
+ * @desc general function to load network's module options,
+ * like update/flush period, wifi alloance
+ * It loads options from VCONF, which should be rewritable or
+ * from network.conf which are RO
+ */
+void load_network_opts(struct net_counter_opts *opts);
+
+/**
+ * @desc move all pids of main pid to the
+ * to appropriate apps cgroups
+ */
+void move_pids_tree_to_cgroup(struct proc_app_info *pai, const char *pkg_name);
+
+#endif /* __RESOURCED_NETSTAT_COMMON_H__ */
--- /dev/null
+/*
+ * 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 datausage-foreach.h
+ *
+ * @desc Data usage foreach initialization/deinitialization functions.
+ *
+ * Created on: Jul 17, 2012
+ */
+
+#ifndef _RESOURCED_SRC_DATAUSAGE_H_
+#define _RESOURCED_SRC_DATAUSAGE_H_
+
+int init_datausage_foreach(sqlite3 *db);
+void finalize_datausage_foreach(void);
+
+#endif /* _RESOURCED_SRC_DATAUSAGE_H_ */
+
--- /dev/null
+/*
+ * 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: datausage-quota-processing.h
+ *
+ * @desc Entity for working with quotas
+ * @version 2.0
+ *
+ * Created on: Aug 08, 2012
+ */
+
+#ifndef _TRESOURCED_DATAUSAGE_QUOTA_PROCESSING_H_
+#define _TRESOURCED_DATAUSAGE_QUOTA_PROCESSING_H_
+
+#include <sqlite3.h>
+#include <stdbool.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 counter_arg *carg);
+
+/*
+ * Finish working with quotas
+ */
+void finalize_quotas(void);
+
+/*
+ * Delete quota and drop remove restriction
+ */
+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_ */
--- /dev/null
+/*
+ * 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: datausage-quota.h
+ *
+ * @desc Performance management API
+ * @version 1.0
+ *
+ * Created on: Jul 31, 2012
+ */
+
+#ifndef TRESOURCED_DATAUSAGE_QUOTA_H_
+#define TRESOURCED_DATAUSAGE_QUOTA_H_
+
+#include <sqlite3.h>
+
+#define RESOURCED_NEW_LIMIT_PATH "db/private/resourced/new_limit"
+#define RESOURCED_DELETE_LIMIT_PATH "db/private/resourced/delete_limit"
+
+int init_datausage_quota(sqlite3 *db);
+
+void finalize_datausage_quota(void);
+
+#endif /* TRESOURCED_DATAUSAGE_QUOTA_H_ */
--- /dev/null
+/*
+ * 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 reset.h
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_RESET_H_
+#define _RESOURCED_RESET_H_
+
+/**
+ * @brief Data usage clearing procedures.
+ * This function deinitializing sqlite3 statements
+ */
+void finalize_datausage_reset(void);
+
+#endif /* _RESOURCED_RESET_H_ */
--- /dev/null
+/*
+ * 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 restriction.h
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_RESTRICTION_H_
+#define _RESOURCED_RESTRICTION_H_
+
+#include <sqlite3.h>
+#include <stdbool.h>
+
+#include "resourced.h"
+#include "data_usage.h"
+#include "transmission.h"
+
+void finalize_datausage_restriction(void);
+
+/**
+ * @desc Update restriction database
+ **/
+resourced_ret_c update_restriction_db(
+ const char *app_id, const resourced_iface_type iftype,
+ const int rcv_limit, const int snd_limit,
+ const resourced_restriction_state rst_state,
+ const int quota_id,
+ const resourced_roaming_type roaming,
+ const char *ifname,
+ const char *imsi);
+
+/**
+ * @desc Get restriction info from database
+ * Now it filles only quota_id, send_limit,
+ * rcv_limit, rst_state
+ *
+ * @param app_id - binpath database field, currently pkgid
+ * @param iftype - iftype database field
+ **/
+resourced_ret_c get_restriction_info(const char *app_id,
+ const resourced_iface_type iftype,
+ resourced_restriction_info *rst);
+
+resourced_ret_c process_kernel_restriction(
+ const u_int32_t classid,
+ const resourced_net_restrictions *rst,
+ 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,
+ bool skip_kernel_op, resourced_restriction_state rst_state);
+
+resourced_ret_c remove_restriction_local(const char *app_id,
+ 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 char *imsi);
+
+#endif /* _RESOURCED_RESTRICTION_H_ */
--- /dev/null
+/*
+ * 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: datausage-vconf-callbacks.h
+ *
+ * @desc Add datausage callback functions to vconf
+ *
+ */
+
+#ifndef _RESOURCED_DATAUSAGE_VCONF_CALLBACKS_H
+#define _RESOURCED_DATAUSAGE_VCONF_CALLBACKS_H
+
+#include "counter.h"
+
+void resourced_add_vconf_datausage_cb(struct counter_arg *carg);
+
+void resourced_remove_vconf_datausage_cb(void);
+
+#endif /* _RESOURCED_DATAUSAGE_VCONF_CALLBACKS_H */
--- /dev/null
+/*
+ * 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.
+ */
+
+#pragma once
+
+enum restriction_state {
+ RESTRICTION_STATE_INIT = -1,
+ RESTRICTION_STATE_UNSET,
+ RESTRICTION_STATE_SET,
+};
+
+resourced_ret_c restriction_check_limit_status(int *retval);
+resourced_ret_c restriction_read_quota(int *quota);
+void restriction_set_status(int value);
--- /dev/null
+/*
+ * 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.h
+ *
+ * @desc This guard procedures are responsible for period db erasing
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_DB_GUARD_H_
+#define _RESOURCED_DB_GUARD_H_
+
+void change_db_entries_num_num(int num);
+
+struct counter_arg;
+resourced_ret_c resourced_init_db_guard(struct counter_arg *carg);
+
+#endif /* _RESOURCED_DB_GUARD_H_ */
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2000 - 2012 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: errors.h
+ *
+ * @desc Internal error codes.
+ * @version 1.0
+ *
+ */
+
+#ifndef _GRABBER_CONTROL_ERRORS_H_
+#define _GRABBER_CONTROL_ERRORS_H_
+
+enum {
+ ERROR_CANT_CREATE_NL_SOCKET = 1,
+ ERROR_UPDATE_PID_LIST = 2,
+ ERROR_UPDATE_CLASSIDS_LIST = 3,
+};
+
+#endif /*_GRABBER_CONTROL_ERRORS_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 generic-netlink.h
+ * @desc Helper function for making kernel request and process response
+ **/
+
+#ifndef _GRABBER_CONTROL_KERNEL_GENERIC_NETLINK_H_
+#define _GRABBER_CONTROL_KERNEL_GENERIC_NETLINK_H_
+
+#include "counter.h"
+#include "genl.h"
+#include "nl-helper.h"
+
+#include <unistd.h>
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+
+enum net_activity_recv {
+ RESOURCED_NET_ACTIVITY_OK,
+ RESOURCED_NET_ACTIVITY_STOP,
+ RESOURCED_NET_ACTIVITY_CONTINUE,
+};
+
+netlink_serialization_command *netlink_create_command(
+ struct netlink_serialization_params *params);
+
+uint32_t get_family_id(int sock, pid_t pid, char *family_name);
+
+/**
+ * @desc get id for multicasted generic netlink messages
+ * only one multicast group is supported per family id now.
+ * This function also gets family id, due it comes with the
+ * same answer as a multicast generic netlink message.
+ */
+uint32_t get_family_group_id(int sock, pid_t pid,
+ char *family_name, char *group_name,
+ uint32_t *family_id);
+
+/**
+ * @desc Extracts family id from answer
+ * accepts opaque pointer
+ **/
+uint32_t netlink_get_family(struct genl *nl_ans);
+
+/**
+ * @desc This function sends to kernel command to start
+ * network activity reporting. This function creats
+ * and closes socket itself.
+ **/
+resourced_ret_c start_net_activity(void);
+
+/**
+ * @desc Stop network activity @see start_net_activity
+ **/
+resourced_ret_c stop_net_activity(void);
+
+struct net_activity_info;
+
+/**
+ * @desc Receive and fill activity info from netlink socket.
+ * Received activity_info should contain the same family_id as
+ * net_activity_family_id
+ */
+enum net_activity_recv recv_net_activity(int sock, struct net_activity_info
+ *activity_info, const uint32_t net_activity_family_id);
+
+/**
+ * @desc Extracts family id from answer
+ * accepts opaque pointer
+ **/
+uint32_t netlink_get_family(struct genl *nl_ans);
+
+void send_start(int sock, const pid_t pid, const int family_id);
+
+int send_command(int sock, const pid_t pid, const int family_id, uint8_t cmd);
+
+int send_restriction(int sock, const pid_t pid, const int family_id,
+ const u_int32_t classid, const int ifindex,
+ const enum traffic_restriction_type restriction_type,
+ const int send_limit,
+ const int rcv_limit,
+ const int snd_warning_threshold,
+ const int rcv_warning_threshold);
+
+resourced_ret_c process_netlink_restriction_msg(const struct genl *ans,
+ struct traffic_restriction *restriction, uint8_t *command);
+
+#endif /*_GRABBER_CONTROL_KERNEL_GENERIC_NETLINK_H_*/
--- /dev/null
+/*
+ * 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 iface-cb.h
+ *
+ * @desc Network interface callbacks entity
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_CALLBACKS_NET_IFACE_H_
+#define _RESOURCED_CALLBACKS_NET_IFACE_H_
+
+int resourced_iface_init(void);
+
+void resourced_iface_finalize(void);
+
+#endif /* _RESOURCED_CALLBACKS_NET_IFACE_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 2000 - 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 iface.h
+ *
+ * @desc Utility for working with network interfaces
+ */
+
+#ifndef TRESOURCED_LIBS_NET_IFACE_H_
+#define TRESOURCED_LIBS_NET_IFACE_H_
+
+#include <glib.h>
+#include <stdbool.h>
+
+#include "config-parser.h"
+#include "data_usage.h"
+#include "macro.h"
+
+/**
+ * @desc Storage now create an instance of this structure
+ */
+typedef struct {
+ void (*handle_iface_up)(int ifindex);
+ void (*handle_iface_down)(int ifindex);
+} iface_callback;
+
+typedef void (*allowance_cb)(resourced_iface_type iftype, bool enabled);
+
+int init_iftype(void);
+void finalize_iftypes(void);
+
+/* 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);
+
+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;
+
+void set_change_allow_cb(allowance_cb cb);
+
+int fill_ifaces_relation(struct parse_result *result,
+ void UNUSED *user_data);
+
+#endif /*TRESOURCED_LIBS_NET_IFACE_H_*/
--- /dev/null
+/*
+ * 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 iptables-rule.h
+ *
+ * @desc Datausage module. This is iptables serialization implemenetation,
+ * intended to be lightweight to support batch operations.
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _IPTABLES_RULE_H
+#define _IPTABLES_RULE_H
+
+#include <glib.h>
+#include <resourced.h>
+#include <netinet/in.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/x_tables.h>
+
+#include "config.h"
+#include "nfacct-rule.h"
+
+struct ipt_context;
+
+enum ipt_verdict_type {
+ IPT_R_STANDARD, /* standard target (ACCEPT, ...) */
+ IPT_R_MODULE, /* extension module (SNAT, ...) */
+ IPT_R_FALLTHROUGH, /* fallthrough rule */
+ IPT_R_JUMP, /* jump to other chain */
+};
+
+struct ipt_chain {
+ char name[XT_FUNCTION_MAXNAMELEN];
+ /*
+ * resourced representation,
+ * in term of iptables it's entries
+ * list of resourced_iptables_entry
+ * */
+ GList *rules;
+ unsigned int num_rules; /* number of rules in list */
+
+ unsigned int head_offset; /* offset in rule blob */
+ unsigned int foot_offset; /* offset in rule blob */
+ unsigned int hooknum; /* hook number+1 if builtin */
+
+ int builtin;
+ int verdict; /* verdict if builtin,
+ for none builtin XT_RETURN will be chosen */
+
+};
+
+#define MAX_CHAIN_INDEX 32
+struct ipt_chain_idx {
+ int foot;
+ int head;
+ struct ipt_chain *chain;
+};
+
+struct ipt_context {
+ char *name;
+ int sock;
+
+ /*
+ * Structure from netfilter_ipv4/ip_tables.h
+ * */
+ struct ipt_getinfo *info;
+ struct ipt_get_entries *blob_entries;
+
+ /*
+ * information about original entries
+ * */
+ size_t old_entries;
+
+ /*
+ * list of chains, INPUT/OUTPUT/FORWARD/user chains here as well
+ * each chain contains list of rules
+ */
+ GList *chains;
+
+ size_t num_chains;
+ struct ipt_chain_idx chain_idx[MAX_CHAIN_INDEX];
+ /*
+ * number of entries
+ */
+ size_t num_entries;
+
+ /*
+ * size in memory
+ */
+ size_t size;
+
+ unsigned int underflow[NF_INET_NUMHOOKS];
+
+ /*
+ * hook_entry[ID] offset to the chain start
+ */
+ unsigned int hook_entry[NF_INET_NUMHOOKS];
+
+ /* original iptables doing the same way */
+ struct ipt_chain* chain_cursor;
+
+ /* was insertion/deletion ? */
+ bool modified;
+};
+
+enum resourced_rule_type {
+ RESOURCED_UNKNOWN_IPT_RULE,
+ RESOURCED_NEW_IPT_RULE, /* rule which was added/allocated */
+ RESOURCED_OLD_IPT_RULE, /* rule which existed */
+ RESOURCED_RULE_LAST_ELEM,
+};
+
+struct resourced_iptables_entry {
+
+ /*
+ * NULL means we are dealing not with nfacct rule,
+ * it's not "our" rule, don't touch it.
+ * TODO: it's possible to grab it from entry,
+ * but need dificult parsing
+ */
+ char nfacct_name[MAX_NAME_LENGTH];
+
+ /*
+ * Structure from netfilter_ipv4/ip_tables.h
+ * This structure defines each of the firewall rules. Consists of 3
+ * parts which are 1) general IP header stuff 2) match specific
+ * stuff 3) the target to perform if the rule matches
+ * It should be just a pointer to blob_entries
+ * */
+ struct ipt_entry *entry;
+
+ /*
+ * entry could chump to chain,
+ * user defined chains e.g. tethering chains
+ */
+ struct ipt_chain *jump;
+ enum ipt_verdict_type verdict_type;
+ int verdict;
+
+ unsigned int offset; /* calculated entry offset in new table */
+ enum resourced_rule_type rule_type;
+};
+
+/* Necessary operations INSERT/DELETE/COMMIT */
+
+/* ipt_start();
+ * ipt_add();/ipt_del() or opt_ops use nfacct_rule_action as part of interface
+ * ipt_commit();
+ */
+
+/*
+ * @desc Start new session. This function obtains list of
+ * rule per table and fill it into ipt context
+ * @param ipt - out parameter, context with list of rules
+ * @param table_name - table name to operate
+ * return RESOURCED_ERROR_NONE in case of success
+ * */
+resourced_ret_c resourced_ipt_begin(struct ipt_context *iptc);
+
+/*
+ * @desc Apply changes to kernel
+ *
+ * */
+resourced_ret_c resourced_ipt_commit(struct ipt_context *iptc);
+
+/*
+ * insert to the begining
+ * */
+resourced_ret_c resourced_ipt_prepend(struct nfacct_rule *rule,
+ struct ipt_context *iptc);
+
+/*
+ * insert at the end
+ * */
+resourced_ret_c resourced_ipt_append(struct nfacct_rule *rule,
+ struct ipt_context *iptc);
+
+/*
+ * remove nfacct rule, rule will be found by cgroup and
+ * nfacct_name match
+ */
+resourced_ret_c resourced_ipt_remove(struct nfacct_rule *rule,
+ struct ipt_context *iptc);
+
+#ifdef NETWORK_DEBUG_ENABLED
+resourced_ret_c resourced_ipt_dump(struct ipt_context *iptc);
+#endif
+
+#endif /* _IPTABLES_RULE_H */
--- /dev/null
+/*
+ * 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: net-cls-cgroup.h
+ *
+ * @desc Performance management daemon. Helper function
+ * for working with classid table.
+ * @version 1.0
+ *
+ */
+
+
+#ifndef _RESOURCED_NET_CLS_CGROUP_H_
+#define _RESOURCED_NET_CLS_CGROUP_H_
+
+#include <sys/types.h>
+#include <glib.h>
+#include <stdbool.h>
+
+#include "resourced.h"
+
+#define PATH_TO_NET_CGROUP_DIR "/sys/fs/cgroup/net_cls"
+
+typedef GArray int_array;
+
+/**
+ * @desc Get all pids from cgroup
+ * Should be invoked after update_classids
+ * @return array, you should free it
+ */
+int_array *get_monitored_pids(void);
+
+/**
+ * @desc update class id - pid table. At present one pid per classid.
+ */
+int update_classids(void);
+
+/**
+ * @desc Get appid from classid task table. At present it is package name.
+ */
+char *get_app_id_by_pid(const pid_t pid);
+char *get_app_id_by_classid(const u_int32_t classid, const bool update_state);
+
+/**
+ * @desc take classid from net_cls cgroup by appid
+ * This function converts appid to pkgname.
+ * @param pkg_name - name of the cgroup
+ * @param create - in case of true - create cgroup if it's not exists
+ * @return classid
+ */
+u_int32_t get_classid_by_app_id(const char *app_id, int create);
+
+/**
+ * @desc create cgroup, generate classid and put classid into cgroup
+ */
+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
+ * generated classid.
+ * If cgroup alreay exists function just put pid in it.
+ * @param pid - process, that will be added to cgroup pkg_name,
+ * @param pkg_name - package name.
+ */
+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_*/
--- /dev/null
+/*
+ * 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 net-restriction.h
+ * @desc Performance management API. Network restriction.
+ * @version 1.0
+ *
+ * Created on: Jun 18, 2012
+ */
+
+#ifndef RESOURCED_NET_RESTRICTION_H_
+#define RESOURCED_NET_RESTRICTION_H_
+
+#include <sys/types.h>
+
+#include "data_usage.h"
+#include "transmission.h"
+
+/**
+ * @brief Send network restriction for specific classid
+ * 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
+ * snd_warning_limit - threshold for warning notification on engress bytes
+ * 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 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 char *ifname);
+
+#endif /* RESOURCED_NET_RESTRICTION_H_ */
--- /dev/null
+/*
+ * resourced
+ *
+ * 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.
+ * 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 nfacct-rule.h
+ *
+ * @desc API for nfacct module
+ *
+ * Copyright (c) 2012-2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_NFACCT_RULE_H
+#define _RESOURCED_NFACCT_RULE_H
+
+#include "const.h"
+#include "data_usage.h"
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define NFACCT_NAME_MAX 32
+
+typedef enum {
+ NFACCT_COUNTER_UNKNOWN,
+ NFACCT_COUNTER_IN = (1 << 1),
+ NFACCT_COUNTER_OUT = (1 << 2),
+ NFACCT_COUNTER_LAST_ELEM
+} nfacct_rule_direction;
+
+typedef enum {
+ NFACCT_ACTION_UNKNOWN,
+ NFACCT_ACTION_APPEND,
+ NFACCT_ACTION_DELETE,
+ NFACCT_ACTION_INSERT,
+ NFACCT_ACTION_LAST_ELEM,
+} nfacct_rule_action;
+
+typedef enum {
+ NFACCT_JUMP_UNKNOWN,
+ NFACCT_JUMP_ACCEPT,
+ NFACCT_JUMP_REJECT,
+ NFACCT_JUMP_LAST_ELEM,
+} nfacct_rule_jump;
+
+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[NFACCT_NAME_MAX];
+ char ifname[MAX_IFACE_LENGTH];
+
+ pid_t pid;
+ u_int32_t classid;
+ resourced_iface_type iftype;
+ nfacct_rule_direction iotype;
+ nfacct_rule_intend intend;
+ 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;
+
+ /*
+ * in most cases jump is evalutation based
+ * on intend, but not always
+ * */
+ nfacct_rule_jump jump;
+};
+
+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_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);
+resourced_ret_c produce_net_rule(struct nfacct_rule *rule,
+ const int send_limit, const int rcv_limit,
+ const nfacct_rule_action action,
+ const nfacct_rule_jump jump,
+ const nfacct_rule_direction iotype);
+
+#endif /* _RESOURCED_NFACCT_RULE_H */
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 nl-helper.h
+ *
+ * @desc Common netlink helper function
+ *
+ * Created on: Jun 25, 2012
+ */
+
+#ifndef RESOURCED_NL_HELPER_H_
+#define RESOURCED_NL_HELPER_H_
+
+#include "app-stat.h"
+/*#include "nfacct-helper.h"*/
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <linux/rtnetlink.h>
+
+#define NLA_BUF_MAX 65560 /*(65 * 1024) - used in tc_common,
+ we'll do the same */
+
+/*TODO: move to common place and rewrite because it's from TC*/
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+/*TODO remove unused code */
+typedef struct {
+ struct nlmsghdr n;
+ struct tcmsg t;
+ char buf[NLA_BUF_MAX];
+} rt_param;
+
+void put_attr(rt_param *arg, int type, const void *data, int data_len);
+
+/*
+ * Generic macros for dealing with netlink sockets. Might be duplicated
+ * elsewhere. It is recommended that commercial grade applications use
+ * libnl or libnetlink and use the interfaces provided by the library
+ */
+#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
+#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
+#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
+
+#define NETLINK_BUF_SIZE 16536
+
+enum nfnl_acct_msg_types {
+ NFNL_MSG_ACCT_NEW,
+ NFNL_MSG_ACCT_GET,
+ NFNL_MSG_ACCT_GET_CTRZERO,
+ NFNL_MSG_ACCT_DEL,
+ NFNL_MSG_ACCT_MAX
+};
+
+enum nfnl_acct_type {
+ NFACCT_UNSPEC,
+ NFACCT_NAME,
+ NFACCT_PKTS,
+ NFACCT_BYTES,
+ NFACCT_USE,
+ NFACCT_FLAGS,
+ NFACCT_QUOTA,
+ NFACCT_FILTER,
+ __NFACCT_MAX
+};
+
+enum nfnl_attr_filter_type {
+ NFACCT_FILTER_ATTR_UNSPEC,
+ NFACCT_FILTER_ATTR_MASK,
+ NFACCT_FILTER_ATTR_VALUE,
+ __NFACCT_FILTER_ATTR_MAX
+};
+
+#define NFACCT_MAX (__NFACCT_MAX - 1)
+
+struct genl {
+ struct nlmsghdr n;
+ struct genlmsghdr g;
+ char buf[NETLINK_BUF_SIZE];
+};
+
+struct netlink_serialization_params {
+ int direction;
+ struct genl *ans;
+ struct counter_arg *carg;
+ int (*eval_attr)(struct rtattr *attr_list[__NFACCT_MAX],
+ void *user_data);
+ int (*post_eval_attr)(void *user_data);
+};
+
+typedef struct {
+ void (*deserialize_answer)(struct netlink_serialization_params *params);
+ void (*finalize)(struct netlink_serialization_params *params);
+ struct netlink_serialization_params params;
+} netlink_serialization_command;
+
+int create_netlink(int protocol, uint32_t groups);
+int read_netlink(int sock, void *buf, size_t len);
+
+void fill_attribute_list(struct rtattr **atb, const int max_len,
+ struct rtattr *rt_na, int rt_len);
+
+#endif /* RESOURCED_NL_HELPER_H_ */
--- /dev/null
+/*
+ * 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.h
+ *
+ * @desc Notification specific functions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef _RESOURCED_DATAUSAGE_NOTIFICATION_H
+#define _RESOURCED_DATAUSAGE_NOTIFICATION_H
+
+#include "data_usage.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 */
--- /dev/null
+/*
+ * 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 restriction-handler.h
+ *
+ * @desc Callback for working reset restrictions
+ */
+
+#ifndef _RESOURCED_RESTRICTION_HANDLER_H_
+#define _RESOURCED_RESTRICTION_HANDLER_H_
+
+#include "iface.h"
+#include "telephony.h"
+
+/**
+ * @brief This function allocates structure
+ * with network up/down handlers.
+ * It's necessary to free memory after usage.
+ */
+iface_callback *create_restriction_callback(void);
+
+void reactivate_restrictions(void);
+
+typedef GList list_restrictions_info;
+
+#endif /* _RESOURCED_RESTRICTION_HANDLER_H_ */
--- /dev/null
+/*
+ * 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 restriction-helper.h
+ * @desc Helper restriction functions
+ */
+
+#ifndef _RESOURCED_RESTRICTION_HELPER_H_
+#define _RESOURCED_RESTRICTION_HELPER_H_
+
+#include "resourced.h"
+#include "transmission.h"
+
+resourced_iface_type get_store_iftype(const u_int32_t app_classid,
+ const resourced_iface_type iftype);
+
+resourced_restriction_state convert_to_restriction_state(
+ const enum traffic_restriction_type rst_type);
+
+enum traffic_restriction_type convert_to_restriction_type(
+ const resourced_restriction_state rst_state);
+
+int check_restriction_arguments(const char *appid,
+ const resourced_net_restrictions *rst,
+ const enum traffic_restriction_type rst_type);
+
+#endif /* _RESOURCED_RESTRICTION_HELPER_H_ */
--- /dev/null
+/*
+ * 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 settings.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef TRESOURCED_LIBS_LOAD_OPTIONS_H_
+#define TRESOURCED_LIBS_LOAD_OPTIONS_H_
+
+#include "data_usage.h"
+
+#define RESOURCED_WIFI_STATISTICS_PATH "db/resourced/wifi_statistics"
+#define RESOURCED_DATACALL_PATH "db/resourced/datacall"
+#define RESOURCED_DATAUSAGE_TIMER_PATH "db/resourced/datausage_timer"
+#define RESOURCED_DATACALL_LOGGING_PATH "db/resourced/datacall_logging"
+
+int load_vconf_net_options(resourced_options *options);
+
+#endif /*TRESOURCED_LIBS_LOAD_OPTIONS_H_*/
--- /dev/null
+/*
+ * 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: specific-trace.h
+ *
+ * @desc Function for print trees for application statistics and for
+ * traffic statistics.
+ * @version 1.0
+ *
+ */
+
+
+#ifndef __PERF_CONTROL_SPECIFIC_TRACE_H__
+#define __PERF_CONTROL_SPECIFIC_TRACE_H__
+
+#include "app-stat.h"
+
+#include "macro.h"
+#include "transmission.h"
+
+gboolean print_appstat(gpointer key, gpointer value, void *data);
+
+#endif /*__PERF_CONTROL_SPECIFIC_TRACE_H__*/
--- /dev/null
+/*
+ * 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: storage.h
+ *
+ * @desc Performance management daemon. Helper function
+ * for working with entity storage.
+ * @version 1.0
+ *
+ */
+
+
+#ifndef _TRAFFIC_CONTROL_TRAFFIC_STAT_STORAGE_H_
+#define _TRAFFIC_CONTROL_TRAFFIC_STAT_STORAGE_H_
+
+#include <sqlite3.h>
+#include <resourced.h>
+
+#include "app-stat.h"
+#include "iface.h"
+
+/**
+ * @desc Initialize database.
+ * At present it tweak "pragma synchronous = off"
+ * and "pragma temp_store = memory"
+ * @param filename - Full path to database
+ */
+resourced_ret_c init_database(const char *filename);
+
+/**
+ * @desc Store result list to database.
+ * @param stats - List of resolved application information
+ * @return 1 if flushed, 0 if not
+ */
+int store_result(struct application_stat_tree *stats);
+
+/**
+ * @desc Just close sqlite statements.
+ */
+void finalize_storage_stm(void);
+
+/**
+ * @desc Return arguments for network interface processing.
+ * Argument contains handler function for react on interface changes.
+ * Changes should be reflect in the database. Whats why it's here.
+ * We doesn't provide special entity for working with database.
+ */
+iface_callback *create_iface_storage_callback(void);
+
+#endif /*_TRAFFIC_CONTROL_TRAFFIC_STAT_STORAGE_H_*/
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 tethering-restriction.h
+ * @desc Performance management API. Tethering restriction.
+ * @version 1.0
+ *
+ * Created on: May 14, 2013
+ */
+
+#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"
+
+/*
+ * @desc Apply tethering restriction for tethering pseudo app
+ */
+resourced_ret_c apply_tethering_restriction(
+ const enum traffic_restriction_type type);
+
+#endif /* RESOURCED_TETHERING_RESTRICTION_H_ */
--- /dev/null
+/*
+ * 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 iptables-rule.c
+ *
+ * @desc Implementation of iptables seriailzation protocol
+ * for nfacct_rule.
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "datausage-common.h"
+#include "iptables-rule.h"
+#include "macro.h"
+#include "trace.h"
+
+#include <resourced.h>
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <string.h>
+
+/* from kernel-headers package */
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/if.h> /* iptables.h requires IFNAMSIZ*/
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter/x_tables.h>
+/* end from kernel-headers package */
+
+
+/*
+ * XTABLES PART
+ * this part is provided only by iptables package
+ */
+
+#define IPT_MIN_IPT_ALIGN (__alignof__(struct ipt_entry))
+
+#define IPT_ALIGN(s) (((s) + ((IPT_MIN_IPT_ALIGN)-1)) & ~((IPT_MIN_IPT_ALIGN)-1))
+
+struct nf_acct;
+
+/*
+ * neither xt_cgroup.h nor xt_nfacct.h is
+ * not placed into iptables-devel package
+ */
+
+struct xt_nfacct_match_info {
+ char name[NFACCT_NAME_MAX];
+ struct nf_acct *nfacct;
+};
+
+struct xt_cgroup_info {
+ __u32 id;
+ __u32 invert;
+};
+
+/* END OF XTABLES PART */
+
+
+#define TABLE_NAME "filter" /* default table name, but it's necessary to specify it */
+#define NFACCT_MATCH_NAME "nfacct"
+#define CGROUP_MATCH_NAME "cgroup"
+
+#define XT_CGROUP_MATCH_SIZE (IPT_ALIGN(sizeof(struct xt_entry_match)) + IPT_ALIGN(sizeof(struct xt_cgroup_info)))
+#define XT_NFACCT_MATCH_SIZE (IPT_ALIGN(sizeof(struct xt_entry_match)) + IPT_ALIGN(sizeof(struct xt_nfacct_match_info)))
+
+#define IPTC_ENTRY_ERROR_TARGET_SIZE (sizeof(struct ipt_entry) + IPT_ALIGN(sizeof(struct xt_error_target)))
+#define IPTC_ENTRY_STANDARD_TARGET_SIZE (sizeof(struct ipt_entry) + IPT_ALIGN(sizeof(struct xt_standard_target)))
+
+#define NFACCT_RULE_SIZE IPT_ALIGN ((sizeof(struct ipt_entry)) + XT_CGROUP_MATCH_SIZE + \
+ XT_NFACCT_MATCH_SIZE + IPT_ALIGN(sizeof(struct xt_standard_target)))
+
+enum ipt_insert_type {
+ IPT_INSERT_APPEND,
+ IPT_INSERT_PREPEND,
+};
+
+static const char *builtin_chains[] = {
+ [NF_IP_PRE_ROUTING] = "PREROUTING",
+ [NF_IP_LOCAL_IN] = "INPUT",
+ [NF_IP_FORWARD] = "FORWARD",
+ [NF_IP_LOCAL_OUT] = "OUTPUT",
+ [NF_IP_POST_ROUTING] = "POSTROUTING",
+};
+
+struct resourced_ipt_entry_info {
+ struct ipt_entry *entry;
+ const char *chain_name;
+ const char *nfacct_name;
+
+ enum resourced_rule_type rule_type;
+ unsigned int hook;
+ size_t size;
+ int builtin;
+ int offset; /* offset in original table */
+};
+
+static struct ipt_entry *get_entry(struct ipt_context *iptc,
+ unsigned int offset)
+{
+ return (struct ipt_entry *)((char *)iptc->blob_entries->entrytable +
+ offset);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+resourced_is_hook_entry(struct ipt_entry *e, struct ipt_context *iptc)
+{
+ unsigned int i;
+
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ if ((iptc->info->valid_hooks & (1 << i))
+ && get_entry(iptc, iptc->info->hook_entry[i]) == e)
+ return i + 1;
+ }
+ return 0;
+}
+
+static resourced_ret_c receive_ipt_items(struct ipt_context *iptc)
+{
+ socklen_t entry_size = sizeof(struct ipt_get_entries) + iptc->info->size;
+
+ int err = getsockopt(iptc->sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
+ iptc->blob_entries, &entry_size);
+ ret_value_msg_if(err < 0, RESOURCED_ERROR_FAIL, "Failed to obtain iptables entries");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/* if no chain with given name, create new one */
+static struct ipt_chain *ipt_select_chain(struct ipt_context *iptc,
+ const char *chain_name,
+ int hook_number)
+{
+ GList *iter = NULL;
+ struct ipt_chain *found_chain = NULL;
+
+ if (chain_name == NULL || hook_number == 0) {
+ _D("Couldn't find proper chain");
+ return NULL;
+ }
+ for (iter = iptc->chains; iter; iter = iter->next) {
+ struct ipt_chain *chain = iter->data;
+ if (!strcmp(chain->name, chain_name)) {
+ found_chain = chain;
+ break;
+ }
+ }
+
+ if (found_chain == NULL) {
+ found_chain = (struct ipt_chain *)malloc(sizeof(struct ipt_chain));
+ ret_value_msg_if(found_chain == NULL, NULL, "Not enough memory!");
+ memset(found_chain, 0, sizeof(struct ipt_chain));
+ strcpy(found_chain->name, chain_name);
+ found_chain->hooknum = hook_number;
+ iptc->chains = g_list_append(iptc->chains,
+ found_chain);
+ }
+
+ return found_chain;
+}
+
+static void recalculate_verdict(struct ipt_chain *chain,
+ struct resourced_iptables_entry *ipt_entry,
+ int *verdict)
+{
+ struct xt_standard_target *t;
+
+ /*
+ * recalculation is necessary only in cases of IPTCC_R_JUMP &&
+ * IPTCC_R_FALLTHROUGH
+ */
+ if (ipt_entry->verdict_type != IPT_R_JUMP &&
+ ipt_entry->verdict_type != IPT_R_FALLTHROUGH)
+ return;
+
+ t = (struct xt_standard_target *)ipt_get_target(ipt_entry->entry);
+ if (ipt_entry->verdict_type == IPT_R_JUMP) {
+ ret_msg_if(ipt_entry->jump == NULL, "Need to find jump destination.");
+
+ memset(t->target.u.user.name, 0, XT_EXTENSION_MAXNAMELEN);
+ strcpy(t->target.u.user.name, XT_STANDARD_TARGET);
+ /*
+ * Jumps can only happen in builtin chains, so we
+ * can safely assume that they always have a header
+ */
+ t->verdict = ipt_entry->jump->head_offset + IPTC_ENTRY_ERROR_TARGET_SIZE;
+ ipt_entry->verdict = t->verdict;
+ } else if (ipt_entry->verdict_type == IPT_R_FALLTHROUGH) {
+ t->verdict = ipt_entry->offset + ipt_entry->entry->next_offset;
+ }
+}
+
+static struct ipt_chain *find_jump_chain(int offset, struct ipt_context *iptc)
+{
+ int pivot = iptc->num_chains/2;
+ int step = 0;
+ while (true) {
+ /* head is lesser than foot */
+ if (iptc->chain_idx[pivot].head <= offset &&
+ offset <= iptc->chain_idx[pivot].foot) {
+ return iptc->chain_idx[pivot].chain;
+ }
+
+ if (pivot == 0 || pivot == iptc->num_chains - 1)
+ break;
+ step = (float)pivot/2 < 1 ? 1 : pivot/2;
+ if (offset < iptc->chain_idx[pivot].head)
+ pivot -= step;
+ if (offset > iptc->chain_idx[pivot].foot)
+ pivot += step;
+ }
+
+ return NULL;
+}
+
+/* find jumps, chain->head_offset/chain->foot_offset should be already calculated */
+static void recalculate_verdicts(struct ipt_context *iptc)
+{
+ struct ipt_chain *chain;
+ struct resourced_iptables_entry *ipt_entry;
+ struct xt_standard_target *std_target;
+ GList *cur_chain, *cur_rule;
+
+ for (cur_chain = iptc->chains; cur_chain; cur_chain = cur_chain->next) {
+ chain = cur_chain->data;
+ for (cur_rule = chain->rules; cur_rule; cur_rule = cur_rule->next) {
+ ipt_entry = cur_rule->data;
+ if (ipt_entry->verdict_type == IPT_R_JUMP) {
+ ipt_entry->jump = find_jump_chain(ipt_entry->verdict, iptc);
+ }
+
+ std_target = (struct xt_standard_target *)ipt_get_target(ipt_entry->entry);
+ recalculate_verdict(chain, ipt_entry, &std_target->verdict);
+ }
+ }
+}
+
+/*
+ * We have many insertions and at last stage need to recalculate
+ * head/foot_offset it will be used to populate underflow/hook_entry
+ * arrays
+ */
+static void build_offset_index(struct ipt_context *ipt)
+{
+ struct ipt_chain *chain;
+ GList *cur_chain, *cur_rule;
+ struct resourced_iptables_entry *ipt_entry;
+ int offset = 0;
+
+ for (cur_chain = ipt->chains; cur_chain; cur_chain = cur_chain->next) {
+ chain = cur_chain->data;
+ chain->head_offset = offset;
+ for (cur_rule = chain->rules; cur_rule; cur_rule = cur_rule->next) {
+ ipt_entry = cur_rule->data;
+ ipt_entry->offset = offset;
+ offset += ipt_entry->entry->next_offset;
+ }
+ /* need take into account removed auxilary entry */
+ if (!chain->builtin) {
+ offset += IPTC_ENTRY_ERROR_TARGET_SIZE;
+ }
+ chain->foot_offset = offset;
+ ipt->chain_idx[ipt->num_chains].chain = chain;
+ /* TODO think maybe keep just chain pointer */
+ ipt->chain_idx[ipt->num_chains].head = chain->head_offset;
+ ipt->chain_idx[ipt->num_chains].foot = chain->foot_offset;
+
+ ipt->num_chains++;
+ if (ipt->num_chains == MAX_CHAIN_INDEX) {
+ _E("Unsupported number of chains. Could not calculate offsets!");
+ return;
+ }
+ offset += sizeof(struct ipt_entry) + IPT_ALIGN(sizeof(struct xt_standard_target));
+ }
+}
+
+/*
+ * need recalculate, every time when new entry is inserted or
+ * removed or once in prepare_replace
+ */
+static void recalculate_final_offset(struct ipt_context *ipt)
+{
+ struct ipt_chain *chain;
+ GList *cur_chain, *cur_rule;
+ struct resourced_iptables_entry *ipt_entry;
+ int offset = 0;
+
+ for (cur_chain = ipt->chains; cur_chain; cur_chain = cur_chain->next) {
+ chain = cur_chain->data;
+ chain->head_offset = offset;
+ for (cur_rule = chain->rules; cur_rule; cur_rule = cur_rule->next) {
+ ipt_entry = cur_rule->data;
+ ipt_entry->offset = offset;
+ offset += ipt_entry->entry->next_offset;
+ }
+ /* need take into account removed auxilary entry */
+ if (!chain->builtin) {
+ offset += IPTC_ENTRY_ERROR_TARGET_SIZE;
+ }
+ chain->foot_offset = offset;
+
+ offset += sizeof(struct ipt_entry) + IPT_ALIGN(sizeof(struct xt_standard_target));
+ }
+}
+
+static resourced_ret_c ipt_populate_entry(struct ipt_context *iptc,
+ struct ipt_entry *entry,
+ const char *nfacct_name,
+ const char *chain_name,
+ const enum resourced_rule_type rule_type,
+ const enum ipt_insert_type insert_type,
+ const int hook_number,
+ const int builtin,
+ const enum ipt_verdict_type verdict_type,
+ const bool is_auxilary)
+{
+ struct resourced_iptables_entry *e = NULL;
+ struct xt_standard_target *std_target = (struct xt_standard_target *)
+ ipt_get_target(entry);
+
+ struct ipt_chain *chain;
+
+ ret_value_msg_if(iptc == NULL ||
+ (chain_name == NULL && hook_number == 0), RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid parameter");
+
+ chain = ipt_select_chain(iptc, chain_name, hook_number);
+
+ if (chain == NULL) {
+ _E("There is no chain with name %s", chain_name);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* move chain cursor */
+ if (chain != iptc->chain_cursor) {
+ iptc->chain_cursor = chain;
+ }
+ /*
+ * chain's builtin couldn't be overwriten, due it was already filled,
+ * by builtin entry
+ */
+ if (!chain->builtin) {
+ chain->builtin = builtin;
+ }
+
+ iptc->num_entries++;
+ iptc->size += entry->next_offset;
+
+ /* it's footer, don't store it */
+ if (is_auxilary) {
+ _D("It's footer entry");
+
+ /*
+ * we need obtain & save verdict from target,
+ * due we no longer keep it
+ */
+ chain->verdict = std_target->verdict;
+ free(entry);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ e = (struct resourced_iptables_entry *)malloc(sizeof(struct resourced_iptables_entry));
+ ret_value_msg_if(e == NULL, RESOURCED_ERROR_FAIL,
+ "Not enough memory");
+ e->entry = entry;
+ e->rule_type = rule_type;
+
+ if (rule_type == RESOURCED_NEW_IPT_RULE)
+ iptc->modified = true;
+
+ if (verdict_type == IPT_R_JUMP) {
+ e->verdict = std_target->verdict;
+ }
+
+ /* it means not nfacct entry */
+ if (nfacct_name)
+ strcpy(e->nfacct_name, nfacct_name);
+ else
+ strcpy(e->nfacct_name, "not nfacct entry"); /* for debug purpose only */
+
+ e->verdict_type = verdict_type;
+ if (insert_type == IPT_INSERT_APPEND)
+ chain->rules = g_list_append(chain->rules, e);
+ else if (insert_type == IPT_INSERT_PREPEND)
+ chain->rules = g_list_prepend(chain->rules, e);
+ else
+ _E("Unknown insert type");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const char *define_chain_name(struct ipt_context *table,
+ int entry_offset, int *hook_number)
+{
+ unsigned int i;
+ for (i = 1; i < NF_INET_NUMHOOKS; i++) {
+ if (table->hook_entry[i] <= entry_offset &&
+ table->underflow[i] >= entry_offset) {
+ *hook_number = i;
+ break;
+ }
+ }
+
+ if (i == NF_INET_NUMHOOKS) {
+ _D("entry %d not in range", entry_offset);
+ /*
+ * it could be user defined chain,
+ * get from current chain
+ */
+ if (table->chain_cursor)
+ return table->chain_cursor->name;
+
+ return NULL;
+ }
+
+ return builtin_chains[i];
+}
+
+enum ipt_verdict_type reverse_target_type(int offset, struct ipt_entry *e)
+{
+ struct xt_standard_target *t = (struct xt_standard_target *)ipt_get_target(e);
+
+ if (!strcmp(t->target.u.user.name, XT_STANDARD_TARGET)) {
+ if (t->target.u.target_size
+ != IPT_ALIGN(sizeof(struct xt_standard_target))) {
+ _E("Mismatch target size for standard target!");
+ return IPT_R_STANDARD;
+ }
+
+ if (t->verdict < 0) {
+ _D("standard, verdict=%d\n", t->verdict);
+ return IPT_R_STANDARD;
+ } else if (t->verdict == offset + e->next_offset) {
+ _D("fallthrough\n");
+ return IPT_R_FALLTHROUGH;
+ } else {
+ _D("jump, target=%u\n", t->verdict);
+ return IPT_R_JUMP;
+ /* Jump target fixup has to be deferred
+ * until second pass, since we migh not
+ * yet have parsed the target */
+ }
+ }
+
+ _D("module, target=%s\n", t->target.u.user.name);
+ return IPT_R_MODULE;
+}
+
+/* works only for incoming blob */
+static inline bool is_last_entry(struct ipt_context *t, struct ipt_entry *e)
+{
+ return (void *)e - (void *)t->blob_entries->entrytable + e->next_offset ==
+ t->blob_entries->size;
+}
+
+static inline bool is_error_target(struct xt_error_target *t)
+{
+ return strcmp(t->target.u.user.name, XT_ERROR_TARGET) == 0;
+}
+
+static void clear_user_chain(struct ipt_context *table)
+{
+ GList *last;
+ if (table->chain_cursor == NULL || table->chain_cursor->builtin)
+ return;
+
+ last = g_list_last(table->chain_cursor->rules);
+ table->chain_cursor->rules = g_list_remove_link(table->chain_cursor->rules,
+ last);
+ free(last->data);
+}
+
+static resourced_cb_ret populate_entry(struct resourced_ipt_entry_info *info,
+ void *user_data)
+{
+ resourced_ret_c ret;
+ struct ipt_context *table = user_data;
+ int hook_number = NF_IP_NUMHOOKS;
+ struct xt_error_target *t = NULL;
+ const char *chain_name;
+ bool is_auxilary_entry = false;
+ int entry_offset;
+ struct ipt_entry *new_entry;
+
+ /*
+ * Last entry is always ERROR TARGET,
+ * which is created by ourself in
+ * prepare_replace
+ */
+ if (is_last_entry(table, info->entry)) {
+ /*
+ * need to remove last entry in none builtin chain,
+ * because it's imposible to determine is it last
+ * for user defined chains
+ */
+ clear_user_chain(table);
+ return RESOURCED_ERROR_NONE;
+ }
+ /*
+ * info->entry should be from table->blobentries->entrytable,
+ * for newly created entry chain name should be predefined by iotype
+ */
+ entry_offset = (int)info->entry - (int)table->blob_entries->entrytable;
+ t = (struct xt_error_target *)ipt_get_target(info->entry);
+
+ /*
+ * not last and it's error target,
+ * so it's new user chain :)
+ */
+ if (is_error_target(t)) {
+ chain_name = (const char *)t->target.data;
+ is_auxilary_entry = true;
+ } else {
+ chain_name = define_chain_name(table, entry_offset, &hook_number);
+ is_auxilary_entry = strcmp(t->target.u.user.name, XT_STANDARD_TARGET) == 0 &&
+ info->entry->target_offset == sizeof(struct ipt_entry) &&
+ info->entry->next_offset == IPTC_ENTRY_STANDARD_TARGET_SIZE &&
+ (hook_number < NF_IP_NUMHOOKS && entry_offset == table->underflow[hook_number]);
+
+ }
+
+#ifdef NETWORK_DEBUG_ENABLED
+ /* just for debug of verdict */
+ if (is_auxilary_entry) {
+ /* trace it */
+ struct xt_standard_target *std_t = (struct xt_standard_target *)t;
+ _D("footer entry->next_offset %d, verdict %d, %s",
+ info->entry->next_offset, std_t->verdict, "AUXILARY ENTRY");
+ }
+#endif /* NETWORK_DEBUG_ENABLED */
+
+ new_entry = (struct ipt_entry *)malloc(info->entry->next_offset);
+
+ ret_value_msg_if(new_entry == NULL, RESOURCED_ERROR_FAIL,
+ "Not enough memory");
+
+ memcpy(new_entry, info->entry, info->entry->next_offset);
+
+ /*
+ * define jump/verdict type,
+ * definition should be based on existing kernel's entry
+ */
+ ret = ipt_populate_entry(table, new_entry, info->nfacct_name, chain_name,
+ info->rule_type, IPT_INSERT_APPEND, hook_number,
+ info->builtin,
+ reverse_target_type(entry_offset, info->entry),
+ is_auxilary_entry);
+ if (ret != RESOURCED_ERROR_NONE) {
+ free(new_entry);
+ return RESOURCED_CANCEL;
+ }
+ return RESOURCED_CONTINUE;
+}
+
+typedef resourced_cb_ret (*iterate_entries_cb)(struct resourced_ipt_entry_info *info, void *user_data);
+
+static int find_nfacct_name (const struct xt_entry_match *match,
+ char **found_name)
+{
+ if (match && !strcmp(match->u.user.name, NFACCT_MATCH_NAME)) {
+ struct xt_nfacct_match_info *info = (struct xt_nfacct_match_info *)match->data;
+ *found_name = info ? info->name: NULL;
+ return 1; /* means stop */
+ }
+ return 0; /* means continue */
+}
+
+/* that function doen't allocate memory */
+static char* extract_nfacct_name(struct ipt_entry *entry)
+{
+ char *found_nfacct_result = NULL;
+ IPT_MATCH_ITERATE(entry, find_nfacct_name, &found_nfacct_result);
+ return found_nfacct_result;
+}
+
+static resourced_ret_c ipt_foreach(struct ipt_context *iptc, iterate_entries_cb cb,
+ void *user_data)
+{
+ struct ipt_entry *entries = iptc->blob_entries->entrytable;
+ /* unsigned int valid_hooks = iptc->info->valid_hooks; */
+ unsigned int *underflow = iptc->info->underflow;
+ unsigned int *hook_entry = iptc->info->hook_entry;
+
+ struct resourced_ipt_entry_info info = {
+ .entry = entries,
+ .size = iptc->blob_entries->size,
+ };
+
+ resourced_cb_ret ret;
+
+ int hook = 0; /* current hook defined by range hook_entry and underflow */
+
+ for (info.offset = 0; info.offset < info.size;
+ info.offset += info.entry->next_offset) {
+ info.rule_type = RESOURCED_OLD_IPT_RULE; /* TODO use enum */
+ info.entry = (void *)entries + info.offset;
+
+ for (;hook < NF_IP_NUMHOOKS; ++hook) {
+ if (hook_entry[hook] <= info.offset &&
+ underflow[hook] > info.offset) {
+ info.hook = hook;
+ break;
+ }
+ }
+ info.nfacct_name = extract_nfacct_name(info.entry);
+
+ info.builtin = resourced_is_hook_entry(info.entry, iptc);
+ if (info.builtin != 0) {
+ _D("built in entry %s", builtin_chains[info.builtin - 1]);
+ }
+
+ ret = cb(&info, iptc);
+ if (ret == RESOURCED_CANCEL)
+ return RESOURCED_ERROR_NONE;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c resourced_ipt_begin(struct ipt_context *iptc)
+{
+ int ret;
+ socklen_t s = sizeof(*iptc->info);
+ ret_value_msg_if(iptc == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Please provide iptc handle");
+ if (iptc->sock == 0) {
+ iptc->sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
+ ret_value_msg_if(iptc->sock < 0, RESOURCED_ERROR_FAIL, "Can't create iptables socket");
+ }
+
+ if (iptc->info == NULL) {
+ iptc->info = (struct ipt_getinfo *)malloc(sizeof(struct ipt_getinfo));
+ if (iptc->info == NULL) {
+ _E("Not enough memory!");
+ goto release_sock;
+ }
+ }
+
+ strcpy(iptc->info->name, TABLE_NAME);
+ ret = getsockopt(iptc->sock, IPPROTO_IP, IPT_SO_GET_INFO,
+ iptc->info, &s);
+
+ if(ret < 0) {
+ _E("iptables support missing error %d (%s)", errno,
+ strerror(errno));
+ goto release_info;
+ }
+
+ /* assume it's second usage and we need realloc */
+ if (iptc->blob_entries) {
+ iptc->blob_entries = (struct ipt_get_entries *)realloc(iptc->blob_entries,
+ sizeof(struct ipt_get_entries) + iptc->info->size);
+ } else {
+ iptc->blob_entries = (struct ipt_get_entries *)malloc(
+ sizeof(struct ipt_get_entries) + iptc->info->size);
+ }
+
+ if (!iptc->blob_entries) {
+ _E("Not enough memory!");
+ goto release_info;
+ }
+
+ memset(iptc->blob_entries, 0, sizeof(struct ipt_get_entries) + iptc->info->size);
+ stpcpy(iptc->blob_entries->name, TABLE_NAME);
+ iptc->blob_entries->size = iptc->info->size;
+
+ /* read into context */
+ ret = receive_ipt_items(iptc);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to receive iptables blob!");
+ goto release_entries;
+ }
+
+ iptc->num_entries = 0;
+ iptc->old_entries = iptc->info->num_entries;
+ iptc->size = 0;
+
+ memcpy(iptc->underflow, iptc->info->underflow,
+ sizeof(iptc->info->underflow));
+ memcpy(iptc->hook_entry, iptc->info->hook_entry,
+ sizeof(iptc->info->hook_entry));
+
+ /* travers on blob to fill internal representation */
+ ipt_foreach(iptc, populate_entry, NULL);
+
+ /*
+ * here, before any insertion/deletion
+ * have occured due it's used by
+ * find_jump_chain which
+ * relays on origintal verdict
+ */
+ build_offset_index(iptc);
+ return RESOURCED_ERROR_NONE;
+release_entries:
+ free(iptc->blob_entries);
+ iptc->blob_entries = NULL;
+release_info:
+ free(iptc->info);
+ iptc->info = NULL;
+release_sock:
+ close(iptc->sock);
+ iptc->sock = 0;
+ return RESOURCED_ERROR_FAIL;
+}
+
+static resourced_ret_c send_ipt_items(struct ipt_context *iptc,
+ struct ipt_replace *r)
+{
+ int err = setsockopt(iptc->sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
+ sizeof(*r) + r->size);
+ ret_value_msg_if(err < 0, RESOURCED_ERROR_FAIL,
+ "Can't send iptables rules! %s [%d]", strerror(errno),
+ errno);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+struct chain_error_placeholder {
+ struct ipt_entry e;
+ struct xt_error_target t;
+};
+
+struct chain_standard_placeholder {
+ struct ipt_entry e;
+ struct xt_standard_target t;
+};
+
+static struct ipt_replace *prepare_replace(struct ipt_context *iptc)
+{
+ struct ipt_replace *r;
+ size_t replace_size = iptc->size + IPTC_ENTRY_ERROR_TARGET_SIZE;
+
+ GList *cur_chain;
+ GList *cur_entry;
+
+ struct chain_error_placeholder *head;
+ struct chain_error_placeholder *tail;
+ struct chain_standard_placeholder *chain_tail;
+
+
+ struct resourced_iptables_entry *e;
+ struct ipt_chain *c;
+ unsigned char *entry_index;
+
+ r = (struct ipt_replace *)malloc(sizeof(struct ipt_replace) + replace_size);
+ ret_value_msg_if(r == NULL, NULL, "Not enough memory");
+ /*
+ * assign iptc->size initial + appended/removed and plus error whole table
+ * footer
+ */
+ memset(r, 0, sizeof(struct ipt_replace) + replace_size);
+
+ r->size = replace_size;
+ /* xt_counters from linux/netfilter/x_ipts.h */
+ r->counters = (struct xt_counters *)malloc(sizeof(struct xt_counters)
+ * iptc->old_entries);
+ if (!r->counters) {
+ free(r);
+ _E("Not enough memory");
+ return NULL;
+ }
+
+ stpcpy(r->name, iptc->info->name);
+ /* append one whole table footer */
+ r->num_entries = iptc->num_entries + 1;
+
+ r->num_counters = iptc->old_entries;
+ r->valid_hooks = iptc->info->valid_hooks;
+
+ recalculate_final_offset(iptc);
+ recalculate_verdicts(iptc);
+
+ entry_index = (unsigned char *)r->entries;
+ for (cur_chain = iptc->chains; cur_chain; cur_chain = cur_chain->next) {
+ c = cur_chain->data;
+ if (!c->builtin) {
+ /* put c header in place */
+ head = (void *)r->entries + c->head_offset;
+ head->e.target_offset = sizeof(struct ipt_entry);
+ head->e.next_offset = IPTC_ENTRY_ERROR_TARGET_SIZE;
+ strcpy(head->t.target.u.user.name, IPT_ERROR_TARGET);
+ head->t.target.u.target_size =
+ IPT_ALIGN(sizeof(struct xt_error_target));
+ strcpy(head->t.errorname, c->name);
+ entry_index += head->e.next_offset;
+ } else {
+ /* current hook_entry[hook] it's underflow[hook - 1] + last->entry->next_offset */
+ r->hook_entry[c->hooknum] = c->head_offset;
+ r->underflow[c->hooknum] = c->foot_offset;
+ }
+
+ _D("c name %s", c->name);
+ for (cur_entry = c->rules; cur_entry; cur_entry = cur_entry->next) {
+ e = cur_entry->data;
+ /*
+ * copy from whatever it was introduced by us in heap or
+ * obtained and stored in entries_blob
+ * e->entry->next_offset it's
+ * Size of ipt_entry + matches + target
+ */
+ memcpy(entry_index, e->entry, e->entry->next_offset);
+ _D("e->entry->next_offset %d, %s", e->entry->next_offset, e->nfacct_name);
+ entry_index += e->entry->next_offset;
+ }
+ /* put chain footer in place */
+ chain_tail = (void *)r->entries + c->foot_offset;
+ chain_tail->e.target_offset = sizeof(struct ipt_entry);
+ chain_tail->e.next_offset = IPTC_ENTRY_STANDARD_TARGET_SIZE;
+ strcpy(chain_tail->t.target.u.user.name, XT_STANDARD_TARGET);
+ chain_tail->t.target.u.target_size =
+ IPT_ALIGN(sizeof(struct xt_standard_target));
+ entry_index += chain_tail->e.next_offset;
+
+ /* builtin targets have verdict, others return */
+ if (c->builtin)
+ chain_tail->t.verdict = c->verdict;
+ else
+ chain_tail->t.verdict = XT_RETURN;
+
+ }
+
+ /* append error target affter all */
+ tail = (void *)(r->entries) + r->size - IPTC_ENTRY_ERROR_TARGET_SIZE;
+ tail->e.target_offset = sizeof(struct ipt_entry);
+ tail->e.next_offset = IPTC_ENTRY_ERROR_TARGET_SIZE;
+ tail->t.target.u.user.target_size =
+ IPT_ALIGN(sizeof(struct xt_error_target));
+ strcpy((char *)&tail->t.target.u.user.name, XT_ERROR_TARGET);
+ strcpy((char *)&tail->t.errorname, XT_ERROR_TARGET);
+
+ return r;
+}
+
+static void free_rules(gpointer data)
+{
+ struct resourced_iptables_entry *entry = (struct resourced_iptables_entry *)data;
+
+ if (!entry)
+ return;
+
+ free(entry->entry);
+ free(entry);
+}
+
+static void free_chains(gpointer data)
+{
+ struct ipt_chain *chain = (struct ipt_chain *)data;
+
+ if (!chain)
+ return;
+
+ g_list_free_full(chain->rules, free_rules);
+ free(chain);
+}
+
+static void ipt_context_release(struct ipt_context *iptc)
+{
+ g_list_free_full(iptc->chains, free_chains);
+
+ free(iptc->blob_entries);
+ free(iptc->info);
+ close(iptc->sock);
+}
+
+API resourced_ret_c resourced_ipt_commit(struct ipt_context *iptc)
+{
+ resourced_ret_c ret_c = RESOURCED_ERROR_NONE;
+ /* structure from /linux/netfilter_ipv4/ip_tables.h */
+ struct ipt_replace *repl;
+
+ if (iptc->modified) {
+ repl = prepare_replace(iptc);
+
+ ret_c = send_ipt_items(iptc, repl);
+
+ free(repl->counters);
+ free(repl);
+ }
+
+ ipt_context_release(iptc);
+ return ret_c;
+}
+
+static void fill_builtin_target(struct nfacct_rule *rule, struct ipt_entry *new_entry)
+{
+ char target_buf[IPT_ALIGN(sizeof(struct xt_standard_target))] = { 0 };
+
+ struct xt_entry_target *entry_target; /* pointer to target area in ipt_entry */
+
+ struct xt_standard_target *res_target = (struct xt_standard_target *)target_buf;
+
+ /*
+ * only builtin target are supported
+ * */
+ strcpy(res_target->target.u.user.name, IPT_STANDARD_TARGET);
+ res_target->target.u.target_size = sizeof(target_buf);
+
+ /*
+ * this offset defines target start position in entry
+ * */
+ new_entry->target_offset = sizeof(struct ipt_entry) + XT_CGROUP_MATCH_SIZE + XT_NFACCT_MATCH_SIZE;
+
+ /*
+ * ipt_get_target makes evaluation based on target_offset
+ * */
+ entry_target = ipt_get_target(new_entry);
+
+ /*
+ * plus IPT_ALIGN(res_target->u.target_size) and it will be whole entry size
+ */
+ new_entry->next_offset = sizeof(struct ipt_entry) + sizeof(target_buf)+
+ XT_CGROUP_MATCH_SIZE + XT_NFACCT_MATCH_SIZE;
+
+ /*
+ * it's not relative it's absolute value,
+ * need to calculate it in build_offset_index
+ */
+ if (rule->jump == NFACCT_JUMP_ACCEPT)
+ res_target->verdict = -NF_ACCEPT - 1;
+ else if (rule->jump == NFACCT_JUMP_REJECT)
+ res_target->verdict = -NF_DROP - 1;
+
+ memcpy(entry_target, res_target, res_target->target.u.target_size);
+}
+
+static void fill_ipt_entry(struct nfacct_rule *rule, struct ipt_entry *entry)
+{
+ char match_buf[XT_NFACCT_MATCH_SIZE] = { 0 };
+ struct xt_entry_match *match = (struct xt_entry_match *)match_buf;
+
+ struct xt_nfacct_match_info nfacct_info;
+ struct xt_cgroup_info cgroup_info = {
+ .id = rule->classid,
+ };
+
+ char *dest_ifname = NULL;
+ unsigned char *dest_ifmask = NULL;
+ /* int iface_flag = 0; TODO necessary to support interface inversion */
+ size_t iface_len = 0;
+
+ memset(entry, 0, NFACCT_RULE_SIZE);
+ if (rule->iotype == NFACCT_COUNTER_IN) {
+ dest_ifname = entry->ip.iniface;
+ dest_ifmask = entry->ip.iniface_mask;
+ /* iface_flag = IPT_INV_VIA_IN;*/
+ }
+ else if (rule->iotype == NFACCT_COUNTER_OUT) {
+ dest_ifname = entry->ip.outiface;
+ dest_ifmask = entry->ip.outiface_mask;
+ /* iface_flag = IPT_INV_VIA_OUT;*/
+ }
+
+ iface_len = strlen(rule->ifname);
+ if (dest_ifname && iface_len) {
+ strcpy(dest_ifname, rule->ifname);
+ memset(dest_ifmask, 0xff, iface_len + 1);
+ }
+
+ strcpy(match->u.user.name, CGROUP_MATCH_NAME);
+ match->u.user.match_size = XT_CGROUP_MATCH_SIZE;
+ memcpy(match->data, &cgroup_info, sizeof(struct xt_cgroup_info));
+ memcpy(entry->elems, match, XT_CGROUP_MATCH_SIZE);
+
+ memset(&nfacct_info, 0, sizeof(struct xt_nfacct_match_info));
+ strcpy(nfacct_info.name, rule->name);
+ strcpy(match->u.user.name, NFACCT_MATCH_NAME);
+ match->u.user.match_size = XT_NFACCT_MATCH_SIZE;
+ memcpy(match->data, &nfacct_info, sizeof(struct xt_nfacct_match_info));
+
+ memcpy(entry->elems + XT_CGROUP_MATCH_SIZE, match,
+ XT_NFACCT_MATCH_SIZE);
+
+ fill_builtin_target(rule, entry);
+}
+
+static int get_hook_by_iotype(struct nfacct_rule *rule)
+{
+ if (rule->iotype == NFACCT_COUNTER_IN)
+ return NF_IP_LOCAL_IN;
+ else if(rule->iotype == NFACCT_COUNTER_OUT)
+ return NF_IP_LOCAL_OUT;
+
+ return NF_IP_NUMHOOKS;
+}
+
+static const char *get_chain_name_by_rule(struct nfacct_rule *rule)
+{
+ return builtin_chains[get_hook_by_iotype(rule)];
+}
+
+static enum ipt_verdict_type get_verdict_type_by_rule(
+ const struct nfacct_rule *rule)
+{
+ if (rule->jump == NFACCT_JUMP_REJECT ||
+ rule->jump == NFACCT_JUMP_ACCEPT)
+ return IPT_R_STANDARD;
+
+ return IPT_R_FALLTHROUGH;
+}
+
+static bool check_existence(struct nfacct_rule *rule, struct ipt_context *iptc)
+{
+ struct resourced_iptables_entry *e = NULL;
+ struct ipt_chain *chain = NULL;
+ GList *cur_chain, *cur_rule;
+ for (cur_chain = iptc->chains; cur_chain; cur_chain = cur_chain->next) {
+ chain = cur_chain->data;
+ for (cur_rule = chain->rules; cur_rule; cur_rule = cur_rule->next) {
+ e = cur_rule->data;
+ if (strcmp (e->nfacct_name, rule->name) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+
+static resourced_ret_c resourced_ipt_insert(struct nfacct_rule *rule,
+ struct ipt_context *iptc,
+ enum ipt_insert_type insert_type)
+{
+ struct ipt_entry *new_entry;
+
+ if (check_existence(rule, iptc)) {
+ _D("rule for %s already exists", rule->name);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ new_entry = (struct ipt_entry *)malloc(NFACCT_RULE_SIZE);;
+
+
+ ret_value_msg_if(new_entry == NULL, RESOURCED_ERROR_FAIL,
+ "Not enough memory");
+
+ /*
+ * each nfacct rule consists of nfacct and cgroup match attribute,
+ * they are represented as xt_entry_match with data of type
+ * xt_cgroup_info and xt_nfacct_info accordingly
+ */
+ fill_ipt_entry(rule, new_entry);
+
+ return ipt_populate_entry(iptc, new_entry, rule->name,
+ get_chain_name_by_rule(rule), RESOURCED_NEW_IPT_RULE,
+ insert_type, get_hook_by_iotype(rule), 1,
+ get_verdict_type_by_rule(rule), false);
+
+}
+
+API resourced_ret_c resourced_ipt_append(struct nfacct_rule *rule,
+ struct ipt_context *iptc)
+{
+ return resourced_ipt_insert(rule, iptc, IPT_INSERT_APPEND);
+}
+
+API resourced_ret_c resourced_ipt_prepend(struct nfacct_rule *rule,
+ struct ipt_context *iptc)
+{
+ return resourced_ipt_insert(rule, iptc, IPT_INSERT_PREPEND);
+}
+
+API resourced_ret_c resourced_ipt_remove(struct nfacct_rule *rule, struct ipt_context *iptc)
+{
+ /*
+ * to delete entry we need:
+ * struct ipt_ip *ip,
+ * const char *chain_name,
+ * const char *target_name,
+ * struct xtables_target *xt_t,
+ * GList *matches,
+ * struct xtables_rule_match *xt_rm
+ */
+
+ /* find entry by rule */
+ struct resourced_iptables_entry *e = NULL, *found_entry = NULL;
+ GList *iter;
+
+ int hook = get_hook_by_iotype(rule);
+ struct ipt_chain *chain = ipt_select_chain(iptc, builtin_chains[hook],
+ hook);
+ if (chain == NULL) {
+ _E("Can't remove entry for %s", rule->name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ for (iter = chain->rules; iter; iter = iter->next) {
+ e = iter->data;
+ if (!strcmp(e->nfacct_name, rule->name)) {
+ found_entry = e;
+ break;
+ }
+ }
+
+ if (found_entry == NULL) {
+ _D("entry for counter %s not found in chain %s", rule->name,
+ chain->name);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ iptc->num_entries--;
+ iptc->size -= found_entry->entry->next_offset;
+
+ chain->rules = g_list_remove(chain->rules, found_entry);
+
+ free(found_entry);
+ iptc->modified = true;
+
+ return RESOURCED_ERROR_NONE;
+}
+
+#ifdef NETWORK_DEBUG_ENABLED
+
+static int dump_each_match(const struct xt_entry_match *match,
+ const struct ipt_ip *ip)
+{
+ if (!strlen(match->u.user.name))
+ return 0;
+
+ _D("\tmatch %s", match->u.user.name);
+ return 0;
+}
+
+static void dump_match(struct ipt_entry *entry)
+{
+ if (entry->target_offset) {
+ IPT_MATCH_ITERATE(entry, dump_each_match, &entry->ip);
+ }
+}
+
+static void dump_ip(struct ipt_entry *entry)
+{
+ struct ipt_ip *ip = &entry->ip;
+ char ip_string[INET6_ADDRSTRLEN];
+ char ip_mask[INET6_ADDRSTRLEN];
+
+ if (strlen(ip->iniface))
+ _D("\tin %s", ip->iniface);
+
+ if (strlen(ip->outiface))
+ _D("\tout %s", ip->outiface);
+
+ if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) &&
+ inet_ntop(AF_INET, &ip->smsk, ip_mask,
+ INET6_ADDRSTRLEN))
+ _D("\tsrc %s/%s", ip_string, ip_mask);
+
+ if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) &&
+ inet_ntop(AF_INET, &ip->dmsk, ip_mask,
+ INET6_ADDRSTRLEN))
+ _D("\tdst %s/%s", ip_string, ip_mask);
+}
+
+static void dump_target(struct ipt_entry *entry)
+
+{
+ struct xt_entry_target *target = ipt_get_target(entry);
+
+ if (strcmp(target->u.user.name, IPT_STANDARD_TARGET) == 0) {
+ struct xt_standard_target *t;
+
+ t = (struct xt_standard_target *)target;
+
+ switch (t->verdict) {
+ case XT_RETURN:
+ _D("\ttarget RETURN");
+ break;
+
+ case -NF_ACCEPT - 1:
+ _D("\ttarget ACCEPT");
+ break;
+
+ case -NF_DROP - 1:
+ _D("\ttarget DROP");
+ break;
+
+ case -NF_QUEUE - 1:
+ _D("\ttarget QUEUE");
+ break;
+
+ case -NF_STOP - 1:
+ _D("\ttarget STOP");
+ break;
+
+ default:
+ _D("\tJUMP %u", t->verdict);
+ break;
+ }
+ }
+}
+
+static resourced_cb_ret dump_entry(struct resourced_ipt_entry_info *info, void *user_data)
+{
+ struct xt_entry_target *target = ipt_get_target(info->entry);
+
+ _D("entry %p next_offset %d ", info->entry,
+ info->entry->next_offset);
+
+ if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
+ _D("\tUSER CHAIN (%s) match %p target %p",
+ target->data, info->entry->elems,
+ (char *)info->entry + info->entry->target_offset);
+
+ return 0;
+ } else if (info->builtin) {
+ _D("\tCHAIN (%s) match %p target %p",
+ info->chain_name, info->entry->elems,
+ (char *)info->entry + info->entry->target_offset);
+ } else {
+ _D("\tRULE match %p target %p",
+ info->entry->elems,
+ (char *)info->entry + info->entry->target_offset);
+ }
+
+ dump_match(info->entry);
+ dump_target(info->entry);
+ dump_ip(info->entry);
+
+ return RESOURCED_CONTINUE;
+}
+
+API resourced_ret_c resourced_ipt_dump(struct ipt_context *iptc)
+{
+ return ipt_foreach(iptc, dump_entry, NULL);
+}
+
+#endif /* NETWORK_DEBUG_ENABLED */
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#include "resourced.h"
+#include "macro.h"
+
+API resourced_ret_c join_app_performance(const char *app_id, const pid_t pid)
+{
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 join.c
+ * @desc Implement Performance API. Joining performance control.
+ * Entity for creation cgroup
+ */
+
+#include <resourced.h>
+
+#include "const.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "trace.h"
+
+static resourced_ret_c send_join_message(const char *interface,
+ const char *format_str, char *params[])
+{
+ DBusError err;
+ DBusMessage *msg;
+ resourced_ret_c ret_val;
+ int ret, i = 0;
+
+ do {
+ msg = dbus_method_sync(RESOURCED_DBUS_BUS_NAME, RESOURCED_PATH_NETWORK,
+ RESOURCED_INTERFACE_NETWORK,
+ interface,
+ format_str, 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 RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &ret_val,
+ DBUS_TYPE_INVALID);
+
+ if (ret == FALSE) {
+ _E("no message : [%s:%s]\n", err.name, err.message);
+ ret_val = RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+
+ return ret_val;
+}
+
+API resourced_ret_c join_app_performance(const char *app_id, const pid_t pid)
+{
+ char *params[2];
+
+ if (!app_id)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ serialize_params(params, ARRAY_SIZE(params), app_id, pid);
+
+ return send_join_message(RESOURCED_NETWORK_JOIN_NET_STAT, "sd", params);
+}
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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 net-restriction.c
+ *
+ * @desc Kernel communication routins for bloking network traffic based on
+ * netlink protocol.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <errno.h>
+#include <glib.h>
+#include <sys/socket.h> /*should be before linux/netlink */
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/netlink.h> /*nlmsghdr */
+#include <linux/pkt_sched.h>
+#include <linux/rtnetlink.h>
+
+#include "resourced.h"
+#include "generic-netlink.h"
+#include "iface.h"
+#include "macro.h"
+#include "netlink-restriction.h"
+#include "nl-helper.h"
+#include "trace.h"
+
+static const char kind_name[] = "htb";
+
+struct rst_context {
+ int sock;
+ int family_id;
+ pid_t pid;
+};
+
+struct nf_arg {
+ u_int32_t classid;
+ enum traffic_restriction_type restriction_type;
+ resourced_iface_type iftype;
+ int error;
+ struct rst_context *context;
+ int send_limit;
+ int rcv_limit;
+ int snd_warning_threshold;
+ int rcv_warning_threshold;
+};
+
+static struct rst_context context;
+
+#if 0
+
+static int send_nl_msg(int sock, pid_t pid, const rt_param *arg)
+{
+ struct sockaddr_nl nladdr = { 0, };
+ struct iovec iov = { 0, };
+ struct msghdr msg = { 0, };
+ int ret = 0;
+
+ if (!arg)
+ return -1;
+
+ iov.iov_base = (void *)(&(arg->n));
+ iov.iov_len = arg->n.nlmsg_len;
+
+ msg.msg_name = &nladdr;
+ msg.msg_namelen = sizeof(nladdr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = pid;
+ nladdr.nl_groups = 0;
+
+ ret = sendmsg(sock, &msg, 0);
+ if (ret < 0)
+ return -errno;
+ return ret;
+}
+
+static int fill_netlink_argument(u_int32_t classid, u_int32_t *seq,
+ int command, int flags, rt_param *argument)
+{
+ if (!argument)
+ return -1;
+
+ memset(argument, 0, sizeof(rt_param));
+
+ argument->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+ argument->n.nlmsg_flags = flags;
+ argument->n.nlmsg_type = command;
+ argument->n.nlmsg_seq = ++(*seq);
+ argument->t.tcm_family = AF_UNSPEC;
+ argument->t.tcm_handle = classid;
+ argument->t.tcm_parent = TC_H_ROOT;
+ argument->t.tcm_ifindex = 2; /*TODO: use iface by sysctl
+ termination, hardcoded eth0 */
+ return 0;
+}
+
+static void add_htb_param(rt_param *arg)
+{
+ struct tc_htb_glob opt = { 0, };
+ struct rtattr *tail = 0;
+
+ opt.rate2quantum = 1;
+ opt.version = 3;
+
+ put_attr(arg, TCA_KIND, kind_name, sizeof(kind_name) + 1);
+ tail = NLMSG_TAIL(&arg->n);
+ put_attr(arg, TCA_OPTIONS, NULL, 0);
+ put_attr(arg, TCA_HTB_INIT, &opt, NLMSG_ALIGN(sizeof(opt)));
+ /*I don't know why but it present in TC */
+ tail->rta_len = (void *)NLMSG_TAIL(&arg->n) - (void *)tail;
+}
+
+static u_int32_t strict_classid(u_int32_t classid)
+{
+ return classid & 0xFFFF0000; /* number: in TC termins */
+}
+
+static int add_root_qdisc(int sock, u_int32_t *seq, pid_t pid)
+{
+ rt_param arg;
+ fill_netlink_argument(0, seq, RTM_NEWQDISC,
+ NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
+ return send_nl_msg(sock, pid, &arg);
+}
+
+/*Create root queue discipline and create base queue discipline*/
+static int add_qdisc(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid)
+{
+ rt_param arg;
+ fill_netlink_argument(classid, seq, RTM_NEWQDISC,
+ NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
+ add_htb_param(&arg);
+ arg.t.tcm_handle = strict_classid(classid);
+ return send_nl_msg(sock, pid, &arg);
+}
+
+/*At present we support only one type of class*/
+static int add_class(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid,
+ int rate_limit)
+{
+ rt_param arg;
+ fill_netlink_argument(classid, seq, RTM_NEWTCLASS,
+ NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
+ return send_nl_msg(sock, pid, &arg);
+}
+
+/*At present we support only one type of filter by cgroup*/
+static int add_filter(int sock, u_int32_t *seq, pid_t pid, u_int32_t classid)
+{
+ rt_param arg;
+ fill_netlink_argument(classid, seq, RTM_NEWTFILTER,
+ NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE, &arg);
+ return send_nl_msg(sock, pid, &arg);
+}
+#endif
+
+static resourced_ret_c send_nf_restriction(int ifindex, resourced_iface_type iftype,
+ void *data)
+{
+ struct nf_arg *nfarg = data;
+
+ if (!nfarg)
+ return RESOURCED_ERROR_FAIL;
+
+ /* use netfilter (ktgrabber) approach */
+ if (nfarg->iftype == iftype) {
+ _D("Sending restriction to kernel:"\
+ " classid %d, ifindex %d, iftype %d, rest_type %d "\
+ " rcv %d, snd %d",
+ nfarg->classid, ifindex, iftype, nfarg->restriction_type,
+ nfarg->rcv_limit, nfarg->send_limit);
+
+ if (send_restriction(nfarg->context->sock, nfarg->context->pid,
+ nfarg->context->family_id, nfarg->classid,
+ ifindex, nfarg->restriction_type,
+ nfarg->send_limit,
+ nfarg->rcv_limit,
+ nfarg->snd_warning_threshold,
+ nfarg->rcv_warning_threshold) < 0) {
+ _E("Failed to sent restriction to kernel");
+ nfarg->error = errno;
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static gboolean send_each_restriction(gpointer key, gpointer value, gpointer data)
+{
+ return send_nf_restriction((int)key,
+ *(resourced_iface_type *)(value), data) == RESOURCED_ERROR_NONE ?
+ FALSE /* Glib continue iteration */ : TRUE;
+}
+
+static resourced_ret_c init_restriction_context(void)
+{
+ if (context.sock)
+ return 0;
+
+ context.sock = create_netlink(NETLINK_GENERIC, 0);
+
+ if (context.sock < 0) {
+ _D("Failed to create and bind netlink socket.");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ context.family_id = get_family_id(context.sock,
+ context.pid, "TRAF_STAT");
+
+ if (context.family_id < 0) {
+ _D("Failed to get family id.");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ context.pid = getpid(); /* for user/kernel space communication */
+ return RESOURCED_ERROR_NONE;
+}
+
+int send_net_restriction(const enum traffic_restriction_type rst_type,
+ 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 char UNUSED *ifname)
+{
+ struct nf_arg nfarg;
+
+ /* initialize context variables */
+ if (init_restriction_context() < 0)
+ return RESOURCED_ERROR_FAIL;
+
+ /* emulate old behaviour, no iftype mean block all network interfaces */
+ if (iftype == RESOURCED_IFACE_UNKNOWN ||
+ iftype == RESOURCED_IFACE_ALL) {
+ _D("Sending restriction to kernel: classid %d, ifindex %d "
+ "iftype %d, restriction_type %d, rcv %d, snd %d\n",
+ classid, RESOURCED_ALL_IFINDEX, iftype, rst_type,
+ send_limit, rcv_limit);
+ return send_restriction(context.sock, context.pid,
+ context.family_id, classid,
+ RESOURCED_ALL_IFINDEX, rst_type,
+ send_limit, rcv_limit,
+ snd_warning_threshold,
+ rcv_warning_threshold);
+ }
+
+ nfarg.context = &context;
+ nfarg.error = 0;
+ nfarg.restriction_type = rst_type;
+ nfarg.iftype = iftype;
+ nfarg.classid = classid;
+ nfarg.send_limit = send_limit;
+ nfarg.rcv_limit = rcv_limit;
+ nfarg.snd_warning_threshold = snd_warning_threshold;
+ nfarg.rcv_warning_threshold = rcv_warning_threshold;
+
+ /* apply a given type of restriction for each network
+ interface of the given network type */
+ init_iftype();
+ for_each_ifindex((ifindex_iterator)send_each_restriction, NULL, &nfarg);
+ return nfarg.error;
+}
--- /dev/null
+/*
+ * 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 main.c
+ * @desc Implement resource API. Initialization routines.
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <sqlite3.h>
+#include <unistd.h>
+
+#include "const.h"
+#include "cgroup.h"
+#include "datausage-foreach.h"
+#include "datausage-quota.h"
+#include "datausage-reset.h"
+#include "datausage-restriction.h"
+#include "macro.h"
+#include "trace.h"
+
+static void __attribute__ ((constructor)) librsml_initialize(void);
+static void __attribute__ ((destructor)) librsml_deinitialize(void);
+
+static sqlite3 *database;
+
+
+static void librsml_initialize(void)
+{
+ _D("librsml_initialize");
+ database = NULL;
+ if (dbus_threads_init_default() != TRUE)
+ _E("Failed to initialize dbus threads support");
+}
+
+#define SQLITE_BUSY_TIMEOUT 500000
+
+static int resourced_db_busy(void UNUSED *user, int attempts)
+{
+ _E("DB locked by another process, attempts number %d", attempts);
+
+ usleep(SQLITE_BUSY_TIMEOUT); /* wait for a half second*/
+ return 1;
+}
+
+API void libresourced_db_initialize_once(void)
+{
+ int res = 0;
+ if (database != NULL)
+ return;
+
+ _D("libresourced_db_initialize_once");
+
+ res = sqlite3_open(DATABASE_FULL_PATH, &database);
+ if (res != SQLITE_OK) {
+ _D("Can't open database %s: %s\n", DATABASE_FULL_PATH,
+ sqlite3_errmsg(database));
+ sqlite3_close(database);
+ 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!");
+ }
+}
+
+static void librsml_deinitialize(void)
+{
+ if (database == NULL)
+ return;
+ finalize_datausage_reset();
+ finalize_datausage_foreach();
+ finalize_datausage_restriction();
+ sqlite3_close(database);
+}
+
+API sqlite3 *resourced_get_database(void)
+{
+ libresourced_db_initialize_once();
+ return database;
+}
--- /dev/null
+/*
+ * 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: net_activity.h
+ *
+ * @desc Handler which get data from generic netlink (NET_ACTIVITY) family
+ */
+
+#include "const.h"
+#include "net-cls-cgroup.h"
+#include "generic-netlink.h"
+#include "iface.h"
+#include "macro.h"
+#include "trace.h"
+
+#include <data_usage.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#define NET_ACTIVITY_LISTEN_TIMEOUT 10
+
+static pthread_t net_activity_worker;
+
+struct net_activity_context {
+ net_activity_cb cb;
+ uint32_t group_family_id;
+ uint32_t family_id;
+ int sock;
+};
+
+static void set_socket_option(int sock)
+{
+ int ret;
+ int opts = fcntl(sock, F_GETFL);
+
+ ret_msg_if(opts < 0, "fcntl error");
+ opts = (opts | O_NONBLOCK);
+
+ ret = fcntl(sock, F_SETFL, opts);
+ ret_msg_if(ret < 0, "fcntl error");
+}
+
+/**
+ * Convert the netlink multicast group id into a bit map
+ * (e.g. 4 => 16, 5 => 32)
+ */
+static uint32_t convert_mcast_group_id(uint32_t group_id)
+{
+ if (group_id > 31) {
+ _E("Netlink: Use setsockopt for this group: %u\n", group_id);
+ return 0;
+ }
+ return group_id ? (1 << (group_id - 1)) : 0;
+}
+
+static void *net_activity_func(void *user_data)
+{
+ int ret, max_fd;
+ struct net_activity_context *context =
+ (struct net_activity_context *)user_data;
+ struct net_activity_info activity_info;
+ struct timeval timeout = {0};
+
+ fd_set rfd;
+ max_fd = context->sock + 1;
+
+ while (1) {
+ timeout.tv_sec = NET_ACTIVITY_LISTEN_TIMEOUT;
+ FD_ZERO(&rfd);
+ FD_SET(context->sock, &rfd);
+
+ ret = select(max_fd, &rfd, NULL, NULL, &timeout);
+
+ if (ret < 0) {
+ _E("Failed to select on generic netlink socket");
+ goto stop_net_activity;
+ }
+
+ if (ret == 0) {
+ _D("timeout");
+ continue;
+ }
+
+ ret = recv_net_activity(context->sock, &activity_info,
+ context->family_id);
+
+ if (ret == RESOURCED_NET_ACTIVITY_STOP)
+ goto stop_net_activity;
+ else if (ret == RESOURCED_NET_ACTIVITY_CONTINUE)
+ continue;
+
+ if (context->cb(&activity_info) == RESOURCED_CANCEL)
+ goto stop_net_activity;
+ }
+
+stop_net_activity:
+ stop_net_activity();
+ close(context->sock);
+ free(context);
+ return NULL;
+
+}
+
+API resourced_ret_c register_net_activity_cb(net_activity_cb activity_cb)
+{
+ int ret;
+ struct net_activity_context *context;
+ pid_t pid;
+
+ ret_value_msg_if(!activity_cb, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Please provide valid callback function!");
+
+ context = (struct net_activity_context *)malloc(
+ sizeof(struct net_activity_context));
+
+ ret_value_if(!context, RESOURCED_ERROR_OUT_OF_MEMORY);
+
+ context->cb = activity_cb;
+ ret = update_classids();
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("Failed to update appid!");
+ goto free_context;
+ }
+
+ context->sock = create_netlink(NETLINK_GENERIC, 0);
+
+ if (context->sock < 0) {
+ _E("Cant create socket");
+ goto free_context;
+ }
+
+ set_socket_option(context->sock);
+ pid = getpid();
+ /* Initialize family id to communicate with NET_ACTIVITY chanel */
+ context->group_family_id = get_family_group_id(context->sock,
+ pid, "NET_ACTIVITY", "NET_ACT_MCAST", &context->family_id);
+
+ start_net_activity();
+
+ if (context->family_id == 0 || context->group_family_id == 0) {
+ _E("Cant get family id");
+ goto close_socket;
+ }
+
+ /* this one is no more needed */
+ close(context->sock);
+
+ /* New one subscribed to group_family_id */
+ context->sock = create_netlink(NETLINK_GENERIC,
+ convert_mcast_group_id(context->group_family_id));
+
+ if (context->sock < 0) {
+ _E("Failed to create multicast socket!");
+ goto free_context;
+ }
+
+ /* start thread */
+ pthread_create(&net_activity_worker, NULL, net_activity_func,
+ (void *)context);
+
+ return RESOURCED_ERROR_NONE;
+
+close_socket:
+ close(context->sock);
+
+free_context:
+ free(context);
+
+ stop_net_activity();
+ return RESOURCED_ERROR_FAIL;
+}
--- /dev/null
+/*
+ * 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 classid-helper.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <dirent.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "appid-helper.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"
+
+#define CUR_CLASSID_PATH "/tmp/cur_classid"
+#define CLASSID_FILE_NAME "/net_cls.classid"
+
+struct task_classid {
+ GArray *pids;
+ int pid_count;
+ u_int32_t classid;
+ char cgroup_name[MAX_NAME_LENGTH]; /*in combination it's package name */
+};
+
+typedef GArray task_classid_array;
+static task_classid_array *tasks_classids;
+
+static int read_uint(FILE *handler, u_int32_t *out)
+{
+ return fscanf(handler, "%u", out);
+}
+
+static int read_int(FILE *handler, int *out)
+{
+ return fscanf(handler, "%d", out);
+}
+
+static u_int32_t produce_classid(void)
+{
+ u_int32_t classid = RESOURCED_RESERVED_CLASSID_MAX;
+ int ret = fread_uint(CUR_CLASSID_PATH, &classid);
+ if (ret < 0)
+ ETRACE_RET_ERRCODE_MSG(ret, "Can not read current classid");
+ ret = fwrite_uint(CUR_CLASSID_PATH, ++classid);
+ if (ret < 0)
+ ETRACE_RET_ERRCODE_MSG(ret, "Can not write classid");
+
+ return classid;
+}
+
+static int place_classid_to_cgroup(const char *cgroup, const char *subdir,
+ u_int32_t *classid)
+{
+ char buf[MAX_PATH_LENGTH];
+ 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, result_classid);
+}
+
+static u_int32_t get_classid_from_cgroup(const char *cgroup, const char *subdir)
+{
+ char buf[MAX_PATH_LENGTH];
+ u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
+ snprintf(buf, sizeof(buf), "%s/%s", cgroup, subdir);
+
+ int ret = cgroup_read_node(buf, CLASSID_FILE_NAME, &classid);
+ if (ret < 0)
+ ETRACE_RET_ERRCODE_MSG(ret, "Cant read classid from cgroup %s",
+ buf);
+ return classid;
+}
+
+static void
+populate_classids_with_pids(const char *dir_name_buf, size_t dir_name_buf_len,
+ const char *cgroup_name_buf,
+ task_classid_array **tasks_classids_list)
+{
+ char file_name_buf[MAX_PATH_LENGTH];
+ FILE *handler = 0;
+ struct task_classid tc;
+ memset(&tc, 0, sizeof(struct task_classid));
+ tc.pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
+ pid_t pid_for_read = 0;
+
+ /* first part of path */
+ snprintf(file_name_buf, sizeof(file_name_buf), "%s%s", dir_name_buf,
+ CLASSID_FILE_NAME);
+ handler = fopen(file_name_buf, "r");
+
+ if (!handler) {
+ _E("can't open %s file\n", file_name_buf);
+ return;
+ }
+
+ if (sizeof(tc.cgroup_name) < strlen(cgroup_name_buf))
+ _SE("not enought buffer for %s", cgroup_name_buf);
+ else
+ strncpy(tc.cgroup_name, cgroup_name_buf, sizeof(tc.cgroup_name)-1);
+
+ if (read_uint(handler, &tc.classid) < 0)
+ _E("can't read classid from file %s\n", file_name_buf);
+
+ fclose(handler);
+
+ strncpy(file_name_buf + dir_name_buf_len, TASK_FILE_NAME,
+ dir_name_buf_len + sizeof(TASK_FILE_NAME));
+
+ handler = fopen(file_name_buf, "r");
+
+ if (!handler) {
+ _E("can't open %s file\n", file_name_buf);
+ return;
+ }
+
+ while (read_int(handler, &pid_for_read) >= 0) {
+ tc.pids = g_array_append_val(tc.pids, pid_for_read);
+ ++tc.pid_count;
+ }
+ *tasks_classids_list = g_array_append_val(*tasks_classids_list, tc);
+
+ fclose(handler);
+}
+
+u_int32_t get_classid_by_app_id(const char *app_id, int create)
+{
+ int ret = 0;
+ bool exists;
+ u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
+
+ if (app_id == NULL) {
+ _E("app_id must be not empty");
+ return RESOURCED_UNKNOWN_CLASSID;
+ }
+
+ if (!strcmp(app_id, RESOURCED_ALL_APP))
+ return RESOURCED_ALL_APP_CLASSID;
+
+ if (!strcmp(app_id, TETHERING_APP_NAME))
+ return RESOURCED_TETHERING_APP_CLASSID;
+
+ if (!strcmp(app_id, RESOURCED_BACKGROUND_APP_NAME))
+ return RESOURCED_BACKGROUND_APP_CLASSID;
+
+ /* just read */
+ if (!create)
+ classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR,
+ app_id);
+
+ if (classid != RESOURCED_UNKNOWN_CLASSID)
+ return classid;
+
+ 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,
+ app_id);
+ else
+ ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR,
+ (char *)app_id, &classid);
+ if (ret)
+ goto handle_error;
+
+ return classid;
+
+ handle_error:
+
+ ETRACE_RET_ERRCODE(ret);
+ return RESOURCED_UNKNOWN_CLASSID;
+}
+
+int update_classids(void)
+{
+ DIR *dir;
+ struct dirent entry;
+ struct dirent *result;
+ int ret;
+
+ char file_name_buf[256];
+ size_t path_to_cgroup_dir_len =
+ sizeof(PATH_TO_NET_CGROUP_DIR), file_name_len;
+
+ snprintf(file_name_buf, sizeof(file_name_buf), "%s/", PATH_TO_NET_CGROUP_DIR);
+
+ if (tasks_classids) {
+ array_foreach(tc, struct task_classid, tasks_classids) {
+ g_array_free(tc->pids, TRUE);
+ }
+ g_array_free(tasks_classids, TRUE);
+ }
+
+ tasks_classids = g_array_new(FALSE, FALSE, sizeof(struct task_classid));
+
+ dir = opendir(file_name_buf);
+
+ if (!dir)
+ return ERROR_UPDATE_CLASSIDS_LIST;
+
+ while (!(ret = readdir_r(dir, &entry, &result)) && result != NULL) {
+ if (entry.d_type != DT_DIR || entry.d_name[0] == '.')
+ continue;
+
+ file_name_len = strlen(entry.d_name);
+ if (file_name_len + path_to_cgroup_dir_len >
+ sizeof(file_name_buf)) {
+ _E("not enought buffer size\n");
+ continue;
+ }
+
+ strncpy(file_name_buf + path_to_cgroup_dir_len, entry.d_name,
+ file_name_len + 1);
+
+ populate_classids_with_pids(file_name_buf,
+ path_to_cgroup_dir_len + file_name_len,
+ entry.d_name, &tasks_classids);
+ }
+ closedir(dir);
+
+#ifdef DEBUG_ENABLED
+ _D("class id table updated");
+#endif
+ return 0;
+}
+
+int_array *get_monitored_pids(void)
+{
+ int_array *result = g_array_new(FALSE, FALSE, sizeof(pid_t));
+
+ if (!result) {
+ _D("Out of memory\n");
+ return 0;
+ }
+
+ array_foreach(tc, struct task_classid, tasks_classids) {
+ int i = 0;
+
+ for (; i < tc->pid_count; ++i) {
+ result = g_array_append_val(result,
+ g_array_index(tc->pids, int, i));
+ }
+ }
+ return result;
+}
+
+static char *get_app_id_by_classid_local(const u_int32_t classid)
+{
+ if (classid == RESOURCED_TETHERING_APP_CLASSID)
+ return strndup(TETHERING_APP_NAME, strlen(TETHERING_APP_NAME));
+ array_foreach(tc, struct task_classid, tasks_classids)
+ if (classid == tc->classid)
+ return strndup(tc->cgroup_name, strlen(tc->cgroup_name));
+ return 0;
+}
+
+char *get_app_id_by_classid(const u_int32_t classid, const bool update_state)
+{
+ int ret;
+ char *appid = get_app_id_by_classid_local(classid);
+
+ if (appid)
+ return appid;
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("can't resolve app id");
+#endif
+ if (!update_state)
+ return 0;
+
+ ret = update_classids();
+ ret_value_msg_if(ret, 0, "Can't get appid for %d", classid);
+
+ return get_app_id_by_classid_local(classid);
+}
+
+API resourced_ret_c make_net_cls_cgroup(const char *pkg_name, u_int32_t classid)
+{
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ bool exists = false;
+
+ if (pkg_name == NULL) {
+ _E("package name must be not empty");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ 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,
+ 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 (pkg_name == NULL) {
+ _E("package name must be not empty");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (access(child_buf, F_OK)) {
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("%s of %s is not existed", child_buf, pkg_name);
+#endif
+ 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;
+
+ if (pkg_name == NULL) {
+ _E("package name must be not empty");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!strcmp(pkg_name, RESOURCED_BACKGROUND_APP_NAME))
+ ret = make_net_cls_cgroup(pkg_name, RESOURCED_BACKGROUND_APP_CLASSID);
+ else
+ 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);
+#ifdef DEBUG_ENABLED
+ _SD("pkg: %s; pid: %d\n", pkg_name, pid);
+#endif
+ 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");
+}
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "const.h"
+#include "net-cls-cgroup.h" /* for const */
+
+int main(int argc, char *argv[])
+{
+ char buf[MAX_PATH_LENGTH];
+ if (argc < 2) {
+ return 1;
+ }
+
+ /* kernel already adds symbol '/' before cgroup name */
+ snprintf(buf, sizeof(buf), "%s/%s", PATH_TO_NET_CGROUP_DIR, argv[1]);
+ return rmdir(buf);
+}
--- /dev/null
+ /*
+ * resourced
+ *
+ * Copyright (c) 2000 - 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 network-dummy.c
+ *
+ * @desc Dummy definitions of the data_usage library
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+
+#include "macro.h"
+#include "data_usage.h"
+#include "resourced.h"
+
+API resourced_ret_c set_resourced_options(const resourced_options *options)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c get_resourced_options(resourced_options *options)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c set_net_restriction(const char *app_id,
+ const resourced_net_restrictions *restriction)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c restrictions_foreach(resourced_restriction_cb restriction_cb, void *user_data)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c remove_restriction(const char *app_id)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c exclude_restriction(const char *app_id)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c exclude_restriction_by_iftype(const char *app_id,
+ const resourced_iface_type iftype)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c set_net_exclusion(const char *app_id,
+ const resourced_net_restrictions *rst)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c register_net_activity_cb(net_activity_cb activity_cb)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c resourced_update_statistics(void)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c data_usage_foreach(const data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb, void *user_data)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c data_usage_details_foreach(const char *app_id, data_usage_selection_rule *rule,
+ data_usage_info_cb info_cb, void *user_data)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c reset_data_usage(const data_usage_reset_rule *rule)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c remove_datausage_quota(const struct datausage_quota_reset_rule *rule)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c remove_datausage_quota_by_iftype(const char *app_id,
+ const resourced_iface_type iftype)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c set_datausage_quota(const char *app_id,
+ const data_usage_quota *quota)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c get_restriction_state(const char *pkg_id, resourced_iface_type iftype,
+ resourced_restriction_state *state)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c remove_restriction_by_iftype(const char *app_id,
+ const resourced_iface_type iftype)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c remove_restriction_full(const char *app_id,
+ const resourced_net_restrictions *restriction)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c resourced_remove_restriction(const char *app_id, char *imsi)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c resourced_remove_restriction_by_iftype(const char *app_id,
+ const resourced_iface_type iftype, char *imsi)
+{
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+[General]
+update_period=20 #cycle of getting counters/evaluate quotas and make decision to block
+flush_period=60 #cycle of storing counters to db
+
+[IFACES_TYPE] #TODO rename to IfacesType
+datacall=rmnet
+datacall=pdp
+datacall=seth_w
+
+
--- /dev/null
+/*
+ * 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 nfacct-restriction.c
+ *
+ * @desc Implementation for set up/down restrictions.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "const.h"
+#include "datausage-common.h"
+#include "stdlib.h"
+#include "macro.h"
+#include "module-data.h"
+#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;
+
+ /* 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,
+ NFACCT_ACTION_APPEND, jump,
+ NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT);
+}
+
+static resourced_ret_c revert_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_JUMP_REJECT;
+
+ return produce_net_rule(rule, send_limit, rcv_limit,
+ NFACCT_ACTION_DELETE, jump,
+ NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT);
+
+}
+
+static resourced_ret_c exclude_net_restriction(struct nfacct_rule *rule)
+{
+ /* Idea to remove old counter and insert new one at first position
+ * iptables has following architecture: it gets all entries from kernel
+ * modifies this list and returns it back, without iptables it could be
+ * done for one step, but with iptables cmd 2 steps is necessary */
+ rule->intend = NFACCT_COUNTER;
+ resourced_ret_c ret = produce_net_rule(rule, 0, 0,
+ NFACCT_ACTION_DELETE, NFACCT_JUMP_UNKNOWN,
+ NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT);
+
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret, "Failed to delete");
+
+ return produce_net_rule(rule, 0, 0,
+ NFACCT_ACTION_INSERT, NFACCT_JUMP_UNKNOWN,
+ NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT);
+}
+
+resourced_ret_c send_net_restriction(const enum traffic_restriction_type rst_type,
+ 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 char *ifname)
+{
+ int ret;
+ struct shared_modules_data *m_data = get_shared_modules_data();
+ struct counter_arg *carg;
+ struct nfacct_rule rule = {
+ .name = {0},
+ .ifname = {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;
+ ret_value_msg_if(carg == NULL, RESOURCED_ERROR_FAIL, "Empty counter");
+
+
+ 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) {
+ /* snd_warning_threshold && */
+ if (rcv_warning_threshold) {
+ rule.intend = NFACCT_WARN;
+ ret = apply_net_restriction(&rule,
+ snd_warning_threshold, rcv_warning_threshold);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+ "Can't apply network restriction");
+ }
+ rule.intend = NFACCT_BLOCK;
+ ret = apply_net_restriction(&rule, send_limit, rcv_limit);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+ "Can't apply network restriction");
+ } else if (rst_type == RST_UNSET) {
+ rule.intend = NFACCT_WARN;
+ ret = revert_net_restriction(&rule,
+ snd_warning_threshold, rcv_warning_threshold);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+ "Can't revert network restriction");
+ rule.intend = NFACCT_BLOCK;
+ return revert_net_restriction(&rule, send_limit,
+ rcv_limit);
+ } else if (rst_type == RST_EXCLUDE)
+ return exclude_net_restriction(&rule);
+
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 nfacct-rule.c
+ *
+ * @desc Datausage module
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "const.h"
+#include "counter.h"
+#include "datausage-common.h"
+#include "iface.h"
+#include "macro.h"
+#include "module-data.h"
+#include "nfacct-rule.h"
+#include "nl-helper.h"
+#include "resourced.h"
+#include "trace.h"
+
+#define IPTABLES "/usr/sbin/iptables"
+#define IPTABLES_CHECK "-C"
+#define APPEND "-A"
+#define DELETE "-D"
+#define INSERT "-I"
+
+#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"
+
+
+/* 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"
+
+
+#define NFNL_SUBSYS_ACCT 7
+
+static void prepare_netlink_msg(struct genl *req, int type, int flag)
+{
+ int seq = time(NULL);
+ memset(req, 0, sizeof(struct genl));
+ req->n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req->n.nlmsg_type = (NFNL_SUBSYS_ACCT << 8) | type;
+ req->n.nlmsg_flags = NLM_F_REQUEST | flag;
+ req->n.nlmsg_seq = seq;
+}
+
+static void add_value_attr(struct genl *req, const void *data, int len, int type)
+{
+ int payload;
+ /* get tail */
+ struct nlattr *na = (struct nlattr *)(
+ (char *)req + NLMSG_ALIGN(req->n.nlmsg_len));
+
+ na->nla_type = type;
+ payload = len + NLA_HDRLEN;
+ na->nla_len = payload;
+ memcpy(NLA_DATA(na), data, len);
+ req->n.nlmsg_len += NLMSG_ALIGN(payload);
+}
+
+/*
+ * following 2 function should be used in combination.
+ * start_nest_attr returns nlattr structure, which should be completed by
+ * end_nest_attr,
+ * before these invocations any number of netlink arguments could be inserted
+ * */
+static struct nlattr *start_nest_attr(struct genl *req, uint16_t type)
+{
+ struct nlattr *start = (struct nlattr *)(
+ (char *)req + NLMSG_ALIGN(req->n.nlmsg_len));
+
+ start->nla_type = NLA_F_NESTED | type;
+ req->n.nlmsg_len += NLMSG_ALIGN(sizeof(struct nlattr));
+ return start;
+}
+
+static void end_nest_attr(struct genl *req, struct nlattr *start)
+{
+ start->nla_len = (__u16)(
+ (char *)req + NLMSG_ALIGN(req->n.nlmsg_len) - (char *)start);
+}
+
+static void add_string_attr(struct genl *req, const char *str, int type)
+{
+ add_value_attr(req, str, strlen(str) + 1, type);
+}
+
+static void add_uint64_attr(struct genl *req, const uint64_t v, int type)
+{
+ add_value_attr(req, &v, sizeof(v), type);
+}
+
+/* macros or templare, due uint64 and uint32 is the same functions */
+static void add_uint32_attr(struct genl *req, const uint32_t v, int type)
+{
+ add_value_attr(req, &v, sizeof(v), type);
+}
+
+static resourced_ret_c send_nfacct_request(int sock, struct genl *req)
+{
+ struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
+ int ret = sendto(sock, (char *)(&req->n), req->n.nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+ "Failed to send command to get outgoing traffic");
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static resourced_ret_c nfacct_send_new(struct nfacct_rule *counter)
+{
+ struct genl req;
+
+ prepare_netlink_msg(&req, NFNL_MSG_ACCT_NEW, NLM_F_CREATE | NLM_F_ACK);
+ add_string_attr(&req, counter->name, NFACCT_NAME);
+#ifdef DEBUG_ENABLED
+ _D("counter name %s", counter->name);
+#endif
+ /* padding */
+ add_uint64_attr(&req, 0, NFACCT_PKTS);
+ add_uint64_attr(&req, 0, NFACCT_BYTES);
+ if (counter->quota) {
+#ifdef DEBUG_ENABLED
+ _D("quota bytes %"PRId64, counter->quota);
+#endif
+ add_uint32_attr(&req, htobe32(NFACCT_F_QUOTA_BYTES), NFACCT_FLAGS);
+ add_uint64_attr(&req, htobe64(counter->quota), NFACCT_QUOTA);
+ }
+
+ return send_nfacct_request(counter->carg->sock, &req);
+}
+
+resourced_ret_c nfacct_send_del(struct nfacct_rule *counter)
+{
+ struct genl req;
+
+#ifdef NETWORK_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)
+
+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;
+ 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, htonl(mask),
+ NFACCT_FILTER_ATTR_MASK);
+ 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;
+ prepare_netlink_msg(&req, NFNL_MSG_ACCT_GET,
+ NLM_F_DUMP);
+ return send_nfacct_request(carg->sock, &req);
+}
+
+static nfacct_rule_direction convert_to_iotype(int type)
+{
+ return type < NFACCT_COUNTER_LAST_ELEM && type > NFACCT_COUNTER_UNKNOWN ?
+ type : NFACCT_COUNTER_UNKNOWN;
+}
+
+static resourced_iface_type convert_to_iftype(int type)
+{
+ return type < RESOURCED_IFACE_LAST_ELEM && type > RESOURCED_IFACE_UNKNOWN ?
+ type : RESOURCED_IFACE_UNKNOWN;
+}
+
+bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *cnt)
+{
+ char *iftype_part;
+ char *classid_part;
+ char *io_part;
+ char *ifname_part;
+ char *saveptr;
+ char name[NFACCT_NAME_MAX] = {0}; /* parse buffer to avoid cnt_name modification */
+
+ strncpy(name, cnt_name, sizeof(name)-1);
+ name[NFACCT_NAME_MAX-1] = 0;
+
+ switch (name[0]) {
+ case 'c':
+ cnt->intend = NFACCT_COUNTER;
+ break;
+ case 'w':
+ cnt->intend = NFACCT_WARN;
+ break;
+ case 'r':
+ cnt->intend = NFACCT_BLOCK;
+ break;
+ case 't':
+ cnt->intend = NFACCT_TETH_COUNTER;
+ break;
+ default:
+ return false;
+ }
+
+ 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) {
+ strncpy(cnt->ifname, ifname_buf, sizeof(cnt->ifname)-1);
+ cnt->ifname[sizeof(cnt->ifname)-1] = 0;
+ cnt->iotype = NFACCT_COUNTER_IN;
+ } else {
+ strncpy(ifname_buf, iftype_part + 1, sizeof(ifname_buf)-1); /* +1, due : symbol and
+ til the end of cnt_name */
+ ifname_buf[MAX_IFACE_LENGTH-1] = 0;
+ iface = get_iftype_by_name(ifname_buf);
+ if (iface == RESOURCED_IFACE_DATACALL) {
+ cnt->iotype = NFACCT_COUNTER_OUT;
+ strncpy(cnt->ifname, ifname_buf, sizeof(cnt->ifname)-1);
+ cnt->ifname[sizeof(cnt->ifname)-1] = 0;
+ }
+ }
+
+ 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_r(name, "_", &saveptr);
+ if (io_part != NULL)
+ cnt->iotype = convert_to_iotype(atoi(io_part + 1));
+ else
+ return false;
+
+ iftype_part = strtok_r(NULL, "_", &saveptr);
+ if (iftype_part != NULL)
+ cnt->iftype = convert_to_iftype(atoi(iftype_part));
+ else
+ return false;
+
+ classid_part = strtok_r(NULL, "_", &saveptr);
+ if (classid_part != NULL)
+ cnt->classid = atoi(classid_part);
+ else {
+ cnt->classid = RESOURCED_ALL_APP_CLASSID;
+ return cnt->intend == NFACCT_BLOCK ? true : false;
+ }
+
+ ifname_part = strtok_r(NULL, "\0", &saveptr);
+ if (ifname_part != NULL)
+ STRING_SAVE_COPY(cnt->ifname, ifname_part);
+ else
+ return false;
+
+ return true;
+}
+
+static void _process_answer(struct netlink_serialization_params *params)
+{
+ struct rtattr *na;
+ struct rtattr *attr_list[__NFACCT_MAX] = {0};
+ struct counter_arg *carg = params->carg;
+ struct genl *ans = params->ans;;
+ struct nlmsghdr *nlhdr = &ans->n;
+ int len = GENLMSG_PAYLOAD(nlhdr);
+ int ans_len = carg->ans_len;
+
+ if (len == 0)
+ return;
+
+ /* parse reply message */
+ na = (struct rtattr *)GENLMSG_DATA(ans);
+
+ while (NLMSG_OK(nlhdr, ans_len )) {
+
+ fill_attribute_list(attr_list, NFACCT_MAX,
+ na, len);
+ if (!attr_list[NFACCT_NAME] ||
+ !attr_list[NFACCT_BYTES])
+ goto next;
+ params->eval_attr(attr_list, carg);
+
+next:
+ nlhdr = NLMSG_NEXT(nlhdr, ans_len);
+ if (ans_len < 0)
+ break;
+ na = (struct rtattr *)GENLMSG_DATA(nlhdr);
+ }
+
+ if (params->post_eval_attr)
+ params->post_eval_attr(carg);
+}
+
+netlink_serialization_command *netlink_create_command(
+ struct netlink_serialization_params *params)
+{
+ static netlink_serialization_command command = {0,};
+ command.deserialize_answer = _process_answer;
+ command.params = *params;
+ return &command;
+}
+
+static unsigned int get_args_number(const char *cmd_buf)
+{
+ char *str;
+ unsigned int count = 0;
+
+ for (str = (char *)cmd_buf; *str != '\0'; ++str) {
+ if (*str == ' ')
+ ++count;
+ }
+ return count;
+}
+
+static void wait_for_rule_cmd(pid_t pid)
+{
+ int status;
+ pid_t ret_pid;
+ char buf[256];
+
+ 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_r(errno, buf, sizeof(buf)));
+ }
+}
+
+static char* get_cmd_pos(const char *cmd_buf)
+{
+ char *cmd_pos = strstr(cmd_buf, APPEND);
+ if (!cmd_pos)
+ cmd_pos = strstr(cmd_buf, INSERT);
+
+ return cmd_pos;
+}
+
+static bool is_rule_exists(const char *cmd_buf)
+{
+ size_t buf_len;
+ char *exec_buf;
+ char *cmd_pos = get_cmd_pos(cmd_buf);
+ bool ret = false;
+ if (!cmd_pos)
+ return false;
+
+ buf_len = strlen(cmd_buf) + 1;
+ exec_buf = (char *)malloc(buf_len);
+ if (!exec_buf)
+ return false;
+
+ strncpy(exec_buf, cmd_buf, buf_len);
+ strncpy(exec_buf + (cmd_pos - cmd_buf), IPTABLES_CHECK,
+ sizeof(IPTABLES_CHECK) - 1);
+#ifdef NETWORK_DEBUG_ENABLED
+ _D("check rule %s", exec_buf);
+#endif
+ ret = system(exec_buf) == 0;
+ free(exec_buf);
+ return ret;
+}
+
+resourced_ret_c exec_iptables_cmd(const char *cmd_buf, pid_t *cmd_pid)
+{
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ char *cmd;
+ char *saveptr;
+ unsigned int i;
+ const size_t args_number = get_args_number(cmd_buf);
+ char *args[args_number + 2];
+ int ret;
+ char buf[256];
+
+#ifdef DEBUG_ENABLED
+ _D("executing iptables cmd %s in forked process", cmd_buf);
+#endif
+ ret_value_msg_if(args_number == 0, RESOURCED_ERROR_FAIL, "no arguments");
+
+ if (is_rule_exists(cmd_buf)) {
+ _D("Rule %s already exists", cmd_buf);
+ exit(0);
+ }
+ args[0] = "iptables";
+ cmd = strtok_r((char *)cmd_buf, " ", &saveptr);
+ ret_value_msg_if(cmd == NULL, RESOURCED_ERROR_FAIL, "no arguments");
+ for (i = 1; i <= args_number; ++i) {
+ args[i] = strtok_r(NULL, " ", &saveptr);
+ }
+ args[i] = NULL;
+
+ ret = execv(cmd, args);
+ if (ret) {
+ _E("Can't execute %s: %s",
+ cmd_buf, strerror_r(errno, buf, sizeof(buf)));
+ }
+ exit(ret);
+ }
+
+ *cmd_pid = pid;
+ return RESOURCED_ERROR_NONE;
+}
+
+static char *choose_iftype_name(struct nfacct_rule *rule)
+{
+ return strlen(rule->ifname) != 0 ? rule->ifname :
+ get_iftype_name(rule->iftype);
+}
+
+static resourced_ret_c exec_iface_cmd(const char *pattern, const char *cmd,
+ const char *chain, const char *nfacct, const char *jump,
+ char *iftype_name, pid_t *pid)
+{
+ char block_buf[MAX_PATH_LENGTH];
+ int ret;
+
+ ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
+ "Invalid network interface name argument");
+
+ ret = snprintf(block_buf, sizeof(block_buf), pattern, IPTABLES, cmd, chain,
+ iftype_name, nfacct, jump);
+ ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_FAIL,
+ "Not enough buffer");
+ return exec_iptables_cmd(block_buf, pid);
+}
+
+static resourced_ret_c exec_app_cmd(const char *pattern, const char *cmd,
+ const char *nfacct, const char *jump,
+ const u_int32_t classid, char *iftype_name,
+ pid_t *pid)
+{
+ char block_buf[MAX_PATH_LENGTH];
+ int ret;
+ ret_value_msg_if(iftype_name == NULL, RESOURCED_ERROR_FAIL,
+ "Invalid network interface name argument");
+ ret = snprintf(block_buf, sizeof(block_buf), pattern, IPTABLES, cmd,
+ iftype_name, classid, nfacct, jump);
+ ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_FAIL,
+ "Not enough buffer");
+ return exec_iptables_cmd(block_buf, pid);
+}
+
+static char *get_iptables_cmd(const nfacct_rule_action action)
+{
+ if (action == NFACCT_ACTION_APPEND)
+ return APPEND;
+ else if(action == NFACCT_ACTION_DELETE)
+ return DELETE;
+ else if (action == NFACCT_ACTION_INSERT)
+ return INSERT;
+
+ 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)
+ return ACCEPT_RULE;
+ else if (jump == NFACCT_JUMP_REJECT)
+ return REJECT_RULE;
+
+ return "";
+}
+
+static resourced_ret_c produce_app_rule(struct nfacct_rule *rule,
+ const int send_limit, const int rcv_limit,
+ const nfacct_rule_action action,
+ const nfacct_rule_jump jump,
+ const nfacct_rule_direction iotype)
+{
+ char *set_cmd = get_iptables_cmd(action);
+ char *jump_cmd = get_iptables_jump(jump);
+ char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
+ 3*MAX_DEC_SIZE(int) + 4];
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ pid_t pid = 0;
+
+ /* income part */
+ if (iotype & NFACCT_COUNTER_IN) {
+ rule->quota = rcv_limit;
+ rule->iotype = NFACCT_COUNTER_IN;
+ generate_counter_name(rule);
+
+ /* to support quated counter we need nfacct,
+ * 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,
+ RESOURCED_ERROR_FAIL, "Can't set conditional block for ingress"
+ " traffic, for classid %u, cmd %s, j %s",
+ rule->classid, set_cmd, jump_cmd);
+
+ /* remove in any case */
+ 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(pid);
+ rule->iptables_rule = nfacct_send_del;
+ set_finalize_flag(rule);
+ nfacct_send_get(rule);
+ }
+ }
+
+ 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_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 = 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_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;
+}
+
+static resourced_ret_c produce_iface_rule(struct nfacct_rule *rule,
+ const int send_limit, const int rcv_limit,
+ const nfacct_rule_action action,
+ const nfacct_rule_jump jump,
+ const nfacct_rule_direction iotype)
+{
+ char *set_cmd = get_iptables_cmd(action);
+ char *jump_cmd = get_iptables_jump(jump);
+ char nfacct_buf[sizeof(NFACCT_NAME_MOD) +
+ 3*MAX_DEC_SIZE(int) + 4];
+ resourced_ret_c ret;
+ pid_t pid = 0;
+
+ /* 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) {
+ /* 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 > sizeof(nfacct_buf) || ret < 0, RESOURCED_ERROR_FAIL,
+ "Not enought buffer");
+
+ 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_FAIL, "Can't set conditional block for ingress"
+ " traffic, for iftype %d, cmd %s, j %s",
+ rule->iftype, set_cmd, jump_cmd);
+
+ /* 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->quota = send_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 enough buffer");
+
+ 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);
+ /* 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;
+}
+
+resourced_ret_c produce_net_rule(struct nfacct_rule *rule,
+ const int send_limit, const int rcv_limit,
+ const nfacct_rule_action action,
+ const nfacct_rule_jump jump,
+ const nfacct_rule_direction iotype)
+{
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+
+ if (action == NFACCT_ACTION_APPEND && rule->intend == NFACCT_WARN
+ && !send_limit && !rcv_limit)
+ return RESOURCED_ERROR_NONE;
+
+ if (rule->classid != RESOURCED_ALL_APP_CLASSID)
+ ret = produce_app_rule(rule, send_limit,
+ rcv_limit, action, jump,
+ iotype);
+ else
+ ret = produce_iface_rule(rule, send_limit, rcv_limit,
+ action, jump, iotype);
+
+ return ret;
+}
+
+void generate_counter_name(struct nfacct_rule *counter)
+{
+ char warn_symbol = 'c';
+ 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';
+ snprintf(counter->name, NFACCT_NAME_MAX, "%c%d_%d_%d_%s",
+ warn_symbol, counter->iotype, counter->iftype,
+ counter->classid, counter->ifname);
+}
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 nl-helper.c
+ * @desc Common netlink helper function
+ *
+ * Created on: Jun 25, 2012
+ */
+
+#include "nl-helper.h"
+#include "trace.h"
+
+#include <unistd.h>
+#include <linux/rtnetlink.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * create_netlink(): Create netlink socket and returns it.
+ * Returns: Created socket on success and -1 on failure.
+ */
+int create_netlink(int protocol, uint32_t groups)
+{
+ /**
+ * TODO it's one socket, in future make set of sockets
+ * unique for protocol and groups
+ */
+ int sock;
+ sock = socket(PF_NETLINK, SOCK_RAW, protocol);
+ if (sock < 0)
+ return -EINVAL;
+
+ struct sockaddr_nl src_addr = { 0, };
+
+ src_addr.nl_family = AF_NETLINK;
+ src_addr.nl_groups = groups;
+
+ if (bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+void fill_attribute_list(struct rtattr **atb, const int max_len,
+ struct rtattr *rt_na, int rt_len)
+{
+ int i = 0;
+ while (RTA_OK(rt_na, rt_len)) {
+ if (rt_na->rta_type <= max_len)
+ atb[rt_na->rta_type] = rt_na;
+
+ rt_na = RTA_NEXT(rt_na, rt_len);
+ ++i;
+ if (i >= max_len)
+ break;
+ }
+}
+
+/* read netlink message from socket
+ * return opaque pointer to genl structure */
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+int read_netlink(int sock, void *buf, size_t len)
+{
+ ssize_t ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = len,
+ };
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(sock, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return ret;
+}
+#else
+int read_netlink(int sock, void *buf, size_t len)
+{
+ int ans_len;
+ struct genl *ans = buf;
+
+ ans_len = recv(sock, ans, len, MSG_DONTWAIT);
+ if (ans_len < 0)
+ return 0;
+
+ if (ans->n.nlmsg_type == NLMSG_ERROR)
+ return 0;
+
+ if (!NLMSG_OK((&ans->n), ans_len))
+ return 0;
+
+ return ans_len;
+}
+#endif
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_wearable.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 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"
+
+void check_and_clear_all_noti(void)
+{
+
+}
+
+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;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ snprintf(str_val, sizeof(str_val), "%s", value);
+ snprintf(buf, sizeof(buf), "%d", quota_limit);
+
+ pa[0] = NOTI_KEY;
+ pa[1] = str_val;
+ pa[2] = NOTI_KEY_LIMIT;
+ pa[3] = buf;
+
+ ret = dbus_method_async(SYSTEM_POPUP_BUS_NAME, SYSTEM_POPUP_PATH_SYSTEM,
+ SYSTEM_POPUP_IFACE_SYSTEM, 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");
+ show_restriction_popup(NOTI_VALUE_DISABLED, du_quota);
+}
+
+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 popup");
+ show_restriction_popup(NOTI_VALUE_WARNING, du_quota);
+}
+
--- /dev/null
+/*
+ * 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: daemon-options.c
+ *
+ * @desc Entity for working with daemon options
+ *
+ */
+
+#include "config-parser.h"
+#include "config.h"
+#include "counter.h"
+#include "const.h"
+#include "iface.h"
+#include "macro.h"
+#include "resourced.h"
+#include "settings.h"
+#include "trace.h"
+
+#include <stdlib.h>
+
+#define GENERAL_SECTION "General"
+#define NET_CONF_FILE "/etc/resourced/network.conf"
+#define NET_UPDATE_PERIOD_NAME "update_period"
+#define NET_FLUSH_PERIOD_NAME "flush_period"
+
+static int fill_general_opt(struct parse_result *result,
+ void *user_data)
+{
+ struct net_counter_opts *opts = (struct net_counter_opts *)user_data;
+ if (strcmp(result->section, GENERAL_SECTION))
+ return RESOURCED_ERROR_NONE;
+
+ if (strcmp(result->name, NET_UPDATE_PERIOD_NAME) == 0) {
+ opts->update_period = atoi(result->value);
+ if (opts->update_period == 0) {
+ _D("not valid value %s for %s key", result->value,
+ NET_UPDATE_PERIOD_NAME);
+ /* use default value */
+ opts->update_period = COUNTER_UPDATE_PERIOD;
+ } else
+ _D("update period is %d", opts->update_period);
+ }
+
+ if (strcmp(result->name, NET_FLUSH_PERIOD_NAME) == 0) {
+ opts->flush_period = atoi(result->value);
+ if (opts->flush_period == 0) {
+ _D("not valid value %s for %s key", result->value,
+ NET_FLUSH_PERIOD_NAME);
+ /* use default value */
+ opts->flush_period = COUNTER_FLUSH_PERIOD;
+ } else
+ _D("flush period is %d", opts->flush_period);
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int parse_net_conf(struct parse_result *result,
+ void *user_data)
+{
+ fill_general_opt(result, user_data);
+ return fill_ifaces_relation(result, user_data);
+}
+
+void load_network_opts(struct net_counter_opts *opts)
+{
+ int ret = 0;
+ /* public structure used only for serialization */
+ resourced_net_options options = { 0 };
+
+ ret_msg_if(opts == NULL,
+ "Invalid daemon options argument\n");
+
+ load_vconf_net_options(&options);
+
+ set_wifi_allowance(options.wifi);
+ set_datacall_allowance(options.datacall);
+ /* TODO replace it to set_datacall_logging
+ * get_datacall_loging in datausage_foreach function */
+ /*opts->datacall_logging = options.datacall_logging;*/
+
+ ret = config_parse(NET_CONF_FILE,
+ parse_net_conf, opts);
+ if (ret != 0)
+ _D("Can't parse config file %s",
+ NET_CONF_FILE);
+
+}
--- /dev/null
+/*
+ * 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 options.c
+ *
+ * @desc Implementation of API for option tweaking:
+ * wifi - collect information for wifi
+ * datacall - collect information for packet data
+ * datausage_time - kernel update period
+ *
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <vconf/vconf.h>
+
+#include "macro.h"
+#include "settings.h"
+#include "trace.h"
+#include "resourced.h"
+#include "const.h"
+
+static int save_options(const resourced_options *options)
+{
+ if (!options) {
+ _E("Please provid valid argument!");
+ return -1;
+ }
+
+ if (vconf_set_bool(RESOURCED_WIFI_STATISTICS_PATH,
+ options->wifi == RESOURCED_OPTION_ENABLE ? 1 : 0) != 0) {
+ _D("Can not get WiFi statistics settings");
+ return -1;
+ }
+
+ if (vconf_set_bool(RESOURCED_DATACALL_PATH,
+ options->datacall == RESOURCED_OPTION_ENABLE ? 1 : 0) != 0) {
+ _D("Can not get DataCall settings");
+ return -1;
+ }
+
+ if (vconf_set_int(RESOURCED_DATAUSAGE_TIMER_PATH,
+ options->datausage_timer) != 0) {
+ _D("Can not get DataUsage timer settings");
+ return -1;
+ }
+
+ if (vconf_set_bool(RESOURCED_DATACALL_LOGGING_PATH,
+ options->datacall_logging == RESOURCED_OPTION_ENABLE ? 1 : 0) != 0) {
+ _D("Can not get DataCall logging settings");
+ return -1;
+ }
+ return 0;
+}
+
+API resourced_ret_c set_resourced_options(const resourced_options *options)
+{
+ /* TODO transfer it by DBUS and store */
+ return save_options(options) ? RESOURCED_ERROR_FAIL : RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c get_resourced_options(resourced_options *options)
+{
+ /* TODO get it by DBUS */
+ return load_vconf_net_options(options) ? RESOURCED_ERROR_FAIL : RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 reset.c
+ *
+ * @desc Network statistics reset implementation. This function's clearing
+ * datausage database.
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <sqlite3.h>
+#include <string.h>
+
+#include "database.h"
+#include "data_usage.h"
+#include "datausage-reset.h"
+#include "macro.h"
+#include "trace.h"
+
+#define RESET_ALL "delete from statistics where time_stamp between ? and ?"
+#define RESET_APP "delete from statistics where binpath=? and " \
+ "time_stamp between ? and ? "
+#define RESET_IFACE "delete from statistics where iftype=? and " \
+ "time_stamp between ? and ?"
+#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[5];
+
+#define PREPARE(stm, query) do { \
+ rc = sqlite3_prepare_v2(db, query, -1, &stm, NULL); \
+ if (rc != SQLITE_OK) { \
+ stm = NULL; \
+ finalize_datausage_reset(); \
+ _E("Failed to prepare statement for\"%s\"query" \
+ , query); \
+ return rc; \
+ } \
+} while (0)
+
+static int init_datausage_reset(sqlite3 *db)
+{
+ int rc;
+ static int initialized;
+
+ if (initialized)
+ return SQLITE_OK;
+
+ PREPARE(reset_stms[0], RESET_ALL);
+ 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;
+}
+
+#define FINALIZE(stm) do { \
+ if (stm) { \
+ sqlite3_finalize(stm); \
+ stm = NULL; \
+ } \
+} while (0)
+
+
+void finalize_datausage_reset(void)
+{
+ int i;
+ for (i = 0; i < sizeof(reset_stms) / sizeof(*reset_stms); i++)
+ 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;
+ resourced_ret_c result = RESOURCED_ERROR_NONE;
+ int pos = 1;/* running through positions where to
+ bind parameters in the query */
+
+ if (!rule || !rule->interval)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ 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;
+ }
+ /* pick a statement depending on parameters.
+ See comment for reset_stms */
+ stm = reset_stms[(rule->app_id ? 1 : 0) |
+ (rule->iftype != RESOURCED_IFACE_LAST_ELEM ? 2 : 0)];
+
+ if (rule->app_id && sqlite3_bind_text(stm, pos++, rule->app_id, -1,
+ SQLITE_TRANSIENT) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (rule->iftype != RESOURCED_IFACE_LAST_ELEM &&
+ sqlite3_bind_int(stm, pos++, rule->iftype) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_bind_int64(stm, pos++, rule->interval->from) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+ if (sqlite3_bind_int64(stm, pos++, rule->interval->to) != SQLITE_OK) {
+ result = RESOURCED_ERROR_DB_FAILED;
+ goto out;
+ }
+
+ if (sqlite3_step(stm) != SQLITE_DONE) {
+ _D("Failed to drop collected statistics.");
+ result = RESOURCED_ERROR_DB_FAILED;
+ }
+
+out:
+ sqlite3_reset(stm);
+ return result;
+}
+
--- /dev/null
+/*
+ * 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 restriction-handler.c
+ *
+ * @desc Callback for working reset restrictions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#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 "notification.h"
+#include "restriction-handler.h"
+#include "restriction-helper.h"
+#include "trace.h"
+
+struct restriction_context {
+ int ifindex;
+ list_restrictions_info *restrictions;
+};
+
+static gpointer _create_reset_restriction(
+ const resourced_restriction_info *info, const int ifindex)
+{
+ resourced_iface_type iftype;
+ resourced_restriction_info *res_data;
+
+ iftype = get_iftype(ifindex);
+ if (info->iftype != iftype)
+ return NULL;
+
+ res_data = (resourced_restriction_info *)
+ malloc(sizeof(resourced_restriction_info));
+ if (!res_data) {
+ _E("Malloc of resourced_restriction_info failed\n");
+ return NULL;
+ }
+ res_data->app_id = strndup(info->app_id, strlen(info->app_id));
+ res_data->iftype = iftype;
+ res_data->rcv_limit = info->rcv_limit;
+ res_data->send_limit = info->send_limit;
+ res_data->rst_state = info->rst_state;
+ res_data->quota_id = info->quota_id;
+ res_data->roaming = info->roaming;
+ res_data->imsi = strndup(info->imsi, strlen(info->imsi));
+ return res_data;
+}
+
+static resourced_cb_ret _restriction_iter(
+ const resourced_restriction_info *info, void *user_data)
+{
+ struct restriction_context *context =
+ (struct restriction_context *)(user_data);
+
+ if (!context) {
+ _E("Please provide valid pointer!");
+ return RESOURCED_CONTINUE;
+ }
+
+ _SI("we have restriction for appid %s and check it for ifindex %d\n",
+ info->app_id, context->ifindex);
+#ifdef MULTISIM_FEATURE_ENABLED
+ const char *imsi_hash = get_imsi_hash(get_current_modem_imsi());
+ if (imsi_hash && info->imsi && !strcmp(imsi_hash, info->imsi)) {
+ gpointer data = _create_reset_restriction(info, context->ifindex);
+ if (data)
+ context->restrictions = g_list_prepend(context->restrictions,
+ data);
+ }
+#else
+ if (info->rst_state != RESOURCED_RESTRICTION_EXCLUDED) {
+ gpointer data = _create_reset_restriction(info, context->ifindex);
+ if (data)
+ context->restrictions = g_list_prepend(context->restrictions,
+ data);
+ }
+#endif
+ return RESOURCED_CONTINUE;
+}
+
+enum restriction_apply_type
+{
+ KEEP_AS_IS,
+ UNSET,
+};
+
+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;
+ struct apply_param *param = (struct apply_param *)user_data;
+
+ u_int32_t app_classid = RESOURCED_UNKNOWN_CLASSID;
+ resourced_net_restrictions rst = {0};
+ int error_code = RESOURCED_ERROR_NONE;
+ enum traffic_restriction_type rst_type;
+
+ ret_msg_if(!arg || !param, "Please provide valid pointer!");
+
+ rst.iftype = arg->iftype;
+ rst.send_limit = arg->send_limit;
+ rst.rcv_limit = arg->rcv_limit;
+ rst.roaming = arg->roaming;
+
+ 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);
+
+ 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;
+
+ app_classid = get_classid_by_app_id(arg->app_id, false);
+
+ error_code = process_kernel_restriction(app_classid,
+ &rst, rst_type, arg->quota_id);
+
+ ret_msg_if(error_code != RESOURCED_ERROR_NONE,
+ "restriction type %d failed, error %d\n", rst_type,
+ error_code);
+}
+
+static void _apply_restrictions(const list_restrictions_info *restrictions)
+{
+ struct apply_param param = {.apply_type = KEEP_AS_IS};
+ if (!restrictions) {
+ _D("No restrictions!");
+ return;
+ }
+ g_list_foreach((GList *)restrictions, _reset_restrictions_iter, ¶m);
+}
+
+static void _reset_restrictions(const list_restrictions_info *restrictions)
+{
+ struct apply_param param = {.apply_type = UNSET};
+ if (!restrictions) {
+ _D("No restrictions!");
+ return;
+ }
+ g_list_foreach((GList *)restrictions, _reset_restrictions_iter, ¶m);
+}
+
+static void _free_restriction_iter(gpointer data)
+{
+ resourced_restriction_info *arg = (resourced_restriction_info *)data;
+ if (!arg) {
+ _D("No restrictions!");
+ return;
+ }
+ free((char *)arg->app_id);
+ free((char *)arg->imsi);
+ free(arg);
+ return;
+}
+
+static void _free_reset_restrictions(list_restrictions_info *restrictions)
+{
+ if (!restrictions) {
+ _E("Plese provide valid pointer!");
+ return;
+ }
+ g_list_free_full(restrictions, _free_restriction_iter);
+}
+
+static void process_on_iface_up(const int ifindex)
+{
+ struct restriction_context context = {
+ .restrictions = 0,
+ .ifindex = ifindex,
+ };
+
+ restrictions_foreach(_restriction_iter, &context);
+ if (!context.restrictions) {
+ _D("No restrictions!");
+ return;
+ }
+ _apply_restrictions(context.restrictions);
+ _free_reset_restrictions(context.restrictions);
+}
+
+static void handle_on_iface_up(const int ifindex)
+{
+ process_on_iface_up(ifindex);
+}
+
+static void handle_on_iface_down(const int ifindex)
+{
+ struct restriction_context context = {
+ .restrictions = 0,
+ .ifindex = ifindex,
+ };
+
+ restrictions_foreach(_restriction_iter, &context);
+ if (!context.restrictions) {
+ _D("No restrictions!");
+ return;
+ }
+ _reset_restrictions(context.restrictions);
+ _free_reset_restrictions(context.restrictions);
+ check_and_clear_all_noti();
+}
+
+iface_callback *create_restriction_callback(void)
+{
+ iface_callback *ret_arg = (iface_callback *)
+ malloc(sizeof(iface_callback));
+
+ if (!ret_arg) {
+ _E("Malloc of iface_callback failed\n");
+ return NULL;
+ }
+ ret_arg->handle_iface_up = handle_on_iface_up;
+ ret_arg->handle_iface_down = handle_on_iface_down;
+
+ return ret_arg;
+}
+
+void reactivate_restrictions(void)
+{
+ int i;
+ char buf[256];
+ struct if_nameindex *ids = if_nameindex();
+
+ ret_msg_if(ids == NULL,
+ "Failed to initialize iftype table! errno: %d, %s",
+ errno, strerror_r(errno, buf, sizeof(buf)));
+
+ for (i = 0; ids[i].if_index != 0; ++i) {
+ if (!is_address_exists(ids[i].if_name))
+ continue;
+ process_on_iface_up(ids[i].if_index);
+ }
+
+ if_freenameindex(ids);
+}
--- /dev/null
+/*
+ * 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 restriction-helper.c
+ * @desc Helper restriction functions
+ */
+
+#include "const.h"
+#include "data_usage.h"
+#include "macro.h"
+#include "trace.h"
+#include "transmission.h"
+
+resourced_iface_type get_store_iftype(const u_int32_t app_classid,
+ const resourced_iface_type iftype)
+{
+ /* in general tethering is based on datacall interface */
+ return (app_classid == RESOURCED_TETHERING_APP_CLASSID) ?
+ RESOURCED_IFACE_DATACALL : iftype;
+}
+
+resourced_restriction_state convert_to_restriction_state(
+ const enum traffic_restriction_type rst_type)
+{
+ switch (rst_type) {
+ case RST_SET:
+ return RESOURCED_RESTRICTION_ACTIVATED;
+ case RST_UNSET:
+ return RESOURCED_RESTRICTION_REMOVED;
+ case RST_EXCLUDE:
+ return RESOURCED_RESTRICTION_EXCLUDED;
+ default:
+ return RESOURCED_RESTRICTION_UNKNOWN;
+ }
+}
+
+enum traffic_restriction_type convert_to_restriction_type(
+ const resourced_restriction_state rst_state)
+{
+ switch (rst_state) {
+ case RESOURCED_RESTRICTION_ACTIVATED:
+ return RST_SET;
+ case RESOURCED_RESTRICTION_REMOVED:
+ return RST_UNSET;
+ case RESOURCED_RESTRICTION_EXCLUDED:
+ return RST_EXCLUDE;
+ default:
+ return RST_UNDEFINDED;
+ }
+}
+
+int check_restriction_arguments(const char *appid,
+ const resourced_net_restrictions *rst,
+ const enum traffic_restriction_type rst_type)
+{
+ ret_value_secure_msg_if(!appid, RESOURCED_ERROR_INVALID_PARAMETER,
+ "appid is required argument\n");
+ ret_value_msg_if(
+ rst_type <= RST_UNDEFINDED || rst_type >= RST_MAX_VALUE,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid restriction_type %d\n", rst_type);
+ ret_value_msg_if(!rst, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Restriction should be set\n");
+ ret_value_msg_if(rst->iftype <= RESOURCED_IFACE_UNKNOWN ||
+ rst->iftype >= RESOURCED_IFACE_LAST_ELEM,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid restriction network interface type %d\n",
+ rst->iftype);
+ if (rst_type == RST_SET) {
+ ret_value_msg_if(rst->send_limit < 0,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid send_limit %d\n", rst->send_limit);
+ ret_value_msg_if(rst->rcv_limit < 0,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid rcv_limit %d\n", rst->rcv_limit);
+ ret_value_msg_if(rst->snd_warning_limit < 0,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid snd_warning_limit %d\n",
+ rst->snd_warning_limit);
+ ret_value_msg_if(rst->rcv_warning_limit < 0,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid rcv_warning_limit %d\n",
+ rst->rcv_warning_limit);
+ }
+
+ /* check roaming */
+ ret_value_msg_if(rst->roaming >= RESOURCED_ROAMING_LAST_ELEM,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "roaming is not valid %d", rst->roaming);
+ /* check imsi */
+ ret_value_msg_if(rst->imsi == NULL,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "imsi is not valid");
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 set-restriction.c
+ * @desc Implementation of the set network restriction body
+ */
+
+#include <sqlite3.h>
+#include <stdbool.h>
+#include <resourced.h>
+
+#include "const.h"
+#include "database.h"
+#include "macro.h"
+#include "module-data.h"
+#include "net-cls-cgroup.h"
+#include "netlink-restriction.h"
+#include "init.h"
+#include "restriction-helper.h"
+#include "datausage-restriction.h"
+#include "telephony.h"
+#include "storage.h"
+#include "trace.h"
+#include "tethering-restriction.h"
+#include "datausage-common.h"
+#include "proc-common.h"
+
+#define SET_NET_RESTRICTIONS "REPLACE INTO restrictions " \
+ "(binpath, rcv_limit, send_limit, iftype, rst_state, "\
+ " quota_id, roaming, ifname, imsi) " \
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
+
+#define GET_NET_RESTRICTION "SELECT rcv_limit, send_limit, " \
+ " rst_state, roaming, quota_id, imsi FROM restrictions " \
+ "WHERE binpath = ? AND iftype = ? AND ifname = ?"
+#define GET_NET_RESTRICTION_BY_QUOTA "SELECT rcv_limit, send_limit, " \
+ " rst_state, roaming, ifname, imsi FROM restrictions " \
+ "WHERE binpath = ? AND iftype = ? AND quota_id = ?"
+
+
+#define RESET_RESTRICTIONS "DELETE FROM restrictions " \
+ "WHERE binpath=? AND iftype=? AND imsi = ? AND quota_id = ?"
+
+static sqlite3_stmt *update_rst_stm;
+static sqlite3_stmt *reset_rst_stm;
+
+static resourced_ret_c init_reset_rst(void)
+{
+ resourced_ret_c error_code = RESOURCED_ERROR_NONE;
+ if (reset_rst_stm)
+ return error_code;
+
+ DB_ACTION(sqlite3_prepare_v2
+ (resourced_get_database(), RESET_RESTRICTIONS, -1,
+ &reset_rst_stm, NULL));
+
+ return error_code;
+
+handle_error:
+ _E("Failed to initialize %s", RESET_RESTRICTIONS);
+ return error_code;
+}
+
+static resourced_ret_c reset_restriction_db(const char *app_id,
+ const resourced_iface_type iftype,
+ const char *imsi,
+ const int quota_id)
+{
+ resourced_ret_c error_code = init_reset_rst();
+
+ ret_value_if(error_code != RESOURCED_ERROR_NONE, error_code);
+#ifdef DEBUG_ENABLED
+ _D("app_id %s",app_id);
+ _D("iftype %d", iftype);
+ _D("imsi %s", imsi);
+ _D("quota_id %d", quota_id);
+#endif
+ 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, imsi, -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;
+
+handle_error:
+
+ sqlite3_reset(reset_rst_stm);
+ if (error_code == RESOURCED_ERROR_DB_FAILED)
+ _E("Failed to remove restrictions by network interface %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+
+ return error_code;
+}
+
+static resourced_ret_c init_update_rest_stmt(void)
+{
+ resourced_ret_c error_code = RESOURCED_ERROR_NONE;
+ if (update_rst_stm)
+ return error_code;
+
+ DB_ACTION(sqlite3_prepare_v2
+ (resourced_get_database(), SET_NET_RESTRICTIONS, -1,
+ &update_rst_stm, NULL));
+ return error_code;
+
+handle_error:
+ _E("Failed to initialize %s", SET_NET_RESTRICTIONS);
+ return error_code;
+}
+
+resourced_ret_c update_restriction_db(
+ const char *app_id, const resourced_iface_type iftype,
+ const int rcv_limit, const int snd_limit,
+ const resourced_restriction_state rst_state,
+ const int quota_id,
+ const resourced_roaming_type roaming,
+ const char *ifname,
+ const char *imsi)
+{
+ 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));
+ DB_ACTION(sqlite3_bind_int64(update_rst_stm, 2, rcv_limit));
+ DB_ACTION(sqlite3_bind_int64(update_rst_stm, 3, snd_limit));
+ DB_ACTION(sqlite3_bind_int(update_rst_stm, 4, iftype));
+ 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));
+ DB_ACTION(sqlite3_bind_text(update_rst_stm, 9, imsi, -1, SQLITE_TRANSIENT));
+
+ if (sqlite3_step(update_rst_stm) != SQLITE_DONE)
+ error_code = RESOURCED_ERROR_DB_FAILED;
+
+handle_error:
+
+ sqlite3_reset(update_rst_stm);
+ if (error_code == RESOURCED_ERROR_DB_FAILED)
+ _E("Failed to set network restriction: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+
+ 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,
+ resourced_restriction_info *rst)
+{
+ int rc;
+ resourced_ret_c error_code = RESOURCED_ERROR_NONE;
+ static sqlite3_stmt *stm_ifname;
+ static sqlite3_stmt *stm_quota;
+ sqlite3_stmt *stm = 0;
+ char *imsi = NULL;
+
+ ret_value_msg_if(rst == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Please provide valid restriction argument!");
+
+ ret_value_msg_if(app_id == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Please provide valid app_id argument!");
+#ifdef DEBUG_ENABLED
+ _SD("app_id: %s, iftype: %d, ifname: %s, quota_id: %d ",
+ app_id, iftype, rst->ifname, rst->quota_id);
+#endif
+ 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);
+ switch (rc) {
+ case SQLITE_ROW:
+ 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->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 = strndup((char *)sqlite3_column_text(stm, 4),
+ strlen((char *)sqlite3_column_text(stm, 4)));
+ imsi = (char *)sqlite3_column_text(stm, 5);
+ if (imsi)
+ rst->imsi = strndup(imsi, strlen(imsi));
+
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ default:
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ goto handle_error;
+ }
+ } while (rc == SQLITE_ROW);
+ sqlite3_reset(stm);
+#ifdef DEBUG_ENABLED
+ _D("quota_id: %d, if_name: %s", rst->quota_id, rst->ifname);
+#endif
+ return RESOURCED_ERROR_NONE;
+
+handle_error:
+
+ 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 fill network restriction's: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+
+ return error_code;
+}
+
+static bool check_roaming(const resourced_net_restrictions *rst)
+{
+ resourced_roaming_type roaming;
+ ret_value_msg_if(rst == NULL, false,
+ "Invalid net_restriction pointer, please provide valid argument");
+
+ roaming = get_current_roaming();
+#ifdef DEBUG_ENABLED
+ _D("roaming %d rst->roaming %d", roaming, rst->roaming);
+#endif
+ if (roaming == RESOURCED_ROAMING_UNKNOWN ||
+ rst->roaming == RESOURCED_ROAMING_UNKNOWN) {
+ return false;
+ }
+ return rst->roaming != roaming;
+}
+
+static void process_net_block_state(const enum
+ traffic_restriction_type rst_type)
+{
+ struct shared_modules_data *m_data = get_shared_modules_data();
+
+ if (m_data)
+ set_daemon_net_block_state(rst_type, m_data->carg);
+ else
+ _E("shared modules data is empty");
+}
+
+resourced_ret_c process_kernel_restriction(
+ const u_int32_t classid,
+ const resourced_net_restrictions *rst,
+ const enum traffic_restriction_type rst_type,
+ const int quota_id)
+{
+ int ret = RESOURCED_ERROR_NONE;
+ struct shared_modules_data *m_data;
+ struct counter_arg *carg;
+
+ 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!");
+ ret_value_secure_msg_if(!classid, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Can not determine classid for package %u.\n"
+ "Probably package was not joined to performance "
+ "monitoring\n", classid);
+
+ if (rst_type == RST_EXCLUDE && check_roaming(rst)) {
+ _D("Restriction not applied: rst->roaming %d", rst->roaming);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ /* 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) &&
+ /* 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, quota_id,
+ rst->iftype,
+ rst->send_limit,
+ rst->rcv_limit,
+ rst->snd_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);
+ if (classid == RESOURCED_BACKGROUND_APP_CLASSID) {
+ if (rst_type == RST_UNSET) {
+ foreground_apps(carg);
+ } else {
+ background_apps(carg);
+ }
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static bool check_background_app(const char *app_id, const resourced_state_t state)
+{
+ if (state == RESOURCED_STATE_BACKGROUND &&
+ !strcmp(app_id, RESOURCED_BACKGROUND_APP_NAME)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+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,
+ bool skip_kernel_op, resourced_restriction_state current_state)
+{
+ u_int32_t app_classid = 0;
+ resourced_iface_type store_iftype;
+ resourced_restriction_state rst_state;
+ struct proc_app_info *pai = NULL;
+ const char *imsi_hash;
+ int ret = check_restriction_arguments(app_id, rst, rst_type);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid restriction arguments\n");
+
+ if (check_background_app(app_id, rst->rs_type))
+ app_classid = RESOURCED_BACKGROUND_APP_CLASSID;
+ else
+ app_classid = get_classid_by_app_id(app_id, rst_type != RST_UNSET);
+ if (!skip_kernel_op) {
+ imsi_hash = get_imsi_hash(get_current_modem_imsi());
+ if (imsi_hash && rst->imsi && !strcmp(imsi_hash, rst->imsi)) {
+ ret = process_kernel_restriction(app_classid, rst, rst_type, quota_id);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Can't keep restriction. only update the DB");
+ }
+ }
+
+ store_iftype = get_store_iftype(app_classid, rst->iftype);
+ rst_state = convert_to_restriction_state(rst_type);
+#ifdef DEBUG_ENABLED
+ _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->imsi, rst->rs_type);
+#endif
+ if (!strcmp(app_id, RESOURCED_ALL_APP) &&
+ rst->iftype == RESOURCED_IFACE_ALL)
+ process_net_block_state(rst_type);
+
+ /* in case of SET/EXCLUDE just update state in db, otherwise remove fro
+ * db */
+ pai = find_app_info_by_appid(app_id);
+
+ if (rst_type == RST_UNSET) {
+ ret = reset_restriction_db(app_id, store_iftype, GLOBAL_CONFIG_IMSI,
+ quota_id);
+ if (pai && current_state == RESOURCED_RESTRICTION_EXCLUDED) {
+ make_net_cls_cgroup_with_pid(pai->main_pid, RESOURCED_BACKGROUND_APP_NAME);
+ move_pids_tree_to_cgroup(pai, RESOURCED_BACKGROUND_APP_NAME);
+ }
+ } else {
+ ret = update_restriction_db(app_id, store_iftype,
+ rst->rcv_limit, rst->send_limit,
+ rst_state, quota_id, rst->roaming,
+ rst->ifname, GLOBAL_CONFIG_IMSI);
+ if (pai && rst_type == RST_EXCLUDE) {
+ place_pids_to_net_cgroup(pai->main_pid, app_id);
+ move_pids_tree_to_cgroup(pai, app_id);
+ mark_background(app_id);
+ }
+ }
+ return ret;
+}
+
+resourced_ret_c remove_restriction_local(const char *app_id,
+ const resourced_iface_type iftype,
+ const int quota_id,
+ const char *imsi_hash,
+ const resourced_state_t ground)
+{
+ 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;
+ rst.imsi = imsi_hash;
+ ret = proc_keep_restriction(app_id, quota_id, &rst,
+ RST_UNSET, skip_kernel_op, rst_info.rst_state);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _D("Can't keep restriction");
+ }
+
+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 char *imsi_hash)
+{
+ resourced_net_restrictions rst = { 0 };
+ bool skip_kernel_op = check_event_in_current_modem(imsi_hash, iftype);
+
+ rst.iftype = iftype;
+ rst.ifname = get_iftype_name(rst.iftype);
+ rst.imsi = imsi_hash;
+ return proc_keep_restriction(app_id, quota_id, &rst, RST_EXCLUDE,
+ skip_kernel_op, RESOURCED_RESTRICTION_EXCLUDED);
+}
--- /dev/null
+/*
+ * 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 restriction.c
+ * @desc Implementation of the network restriction
+ */
+
+#include <sqlite3.h>
+#include <resourced.h>
+#include <data_usage.h>
+
+#include "const.h"
+#include "database.h"
+#include "datausage-restriction.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "netlink-restriction.h"
+#include "restriction-helper.h"
+#include "tethering-restriction.h"
+#include "trace.h"
+
+#define SELECT_RESTRICTIONS "SELECT binpath, rcv_limit, " \
+ "send_limit, iftype, rst_state, quota_id, roaming, imsi FROM restrictions"
+
+#define SELECT_RESTRICTION_STATE "SELECT rst_state FROM restrictions " \
+ "WHERE binpath = ? AND iftype = ?"
+
+static sqlite3_stmt *datausage_restriction_select;
+static sqlite3_stmt *restriction_get_state_stmt;
+
+static int init_datausage_restriction(sqlite3 *db)
+{
+ int rc;
+ if (datausage_restriction_select)
+ return SQLITE_OK;
+
+ rc = sqlite3_prepare_v2(db, SELECT_RESTRICTIONS, -1 ,
+ &datausage_restriction_select, NULL);
+ if (rc != SQLITE_OK) {
+ _E("can not prepare datausage_restriction_select\n");
+ datausage_restriction_select = NULL;
+ return rc;
+ }
+ return rc;
+}
+
+static void serialize_restriction(const char *appid,
+ const enum traffic_restriction_type rst_type,
+ const resourced_net_restrictions *rst,
+ char *params[])
+{
+ params[0] = (char *)appid;
+ params[1] = (char *)rst_type;
+ params[2] = (char *)rst->rs_type;
+ params[3] = (char *)rst->iftype;
+ params[4] = (char *)rst->send_limit;
+ params[5] = (char *)rst->rcv_limit;
+ params[6] = (char *)rst->snd_warning_limit;
+ params[7] = (char *)rst->rcv_warning_limit;
+ params[8] = (char *)rst->roaming;
+ params[9] = (char *)rst->imsi;
+}
+
+static resourced_ret_c process_restriction(
+ const char *app_id, const resourced_net_restrictions *rst,
+ const enum traffic_restriction_type rst_type)
+{
+ DBusError err;
+ DBusMessage *msg;
+ char *params[10];
+ int i = 0, ret;
+ resourced_ret_c ret_val;
+
+ ret = check_restriction_arguments(app_id, rst, rst_type);
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
+ RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid restriction arguments\n");
+
+ serialize_restriction(app_id, rst_type, rst, params);
+
+ do {
+ msg = dbus_method_sync(RESOURCED_DBUS_BUS_NAME, RESOURCED_PATH_NETWORK,
+ RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_PROCESS_RESTRICTION,
+ "sdddddddds", 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 RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &ret_val,
+ DBUS_TYPE_INVALID);
+
+ if (ret == FALSE) {
+ _E("no message : [%s:%s]\n", err.name, err.message);
+ ret_val = RESOURCED_ERROR_FAIL;
+ }
+
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+
+ return ret_val;
+}
+
+API resourced_ret_c restrictions_foreach(
+ resourced_restriction_cb restriction_cb, void *user_data)
+{
+ resourced_restriction_info data;
+ int rc;
+ resourced_ret_c error_code = NETWORK_ERROR_NONE;
+
+ libresourced_db_initialize_once();
+ if (init_datausage_restriction(resourced_get_database()) != SQLITE_OK) {
+ _D("Failed to initialize data usage restriction statement: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ do {
+ rc = sqlite3_step(datausage_restriction_select);
+ switch (rc) {
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ROW:
+ data.app_id = (char *)sqlite3_column_text(
+ datausage_restriction_select, 0);
+ data.iftype = (resourced_iface_type)sqlite3_column_int(
+ datausage_restriction_select, 3);
+ data.rcv_limit = sqlite3_column_int(
+ datausage_restriction_select, 1);
+ data.send_limit = sqlite3_column_int(
+ datausage_restriction_select, 2);
+ data.rst_state =
+ (resourced_restriction_state)sqlite3_column_int(
+ datausage_restriction_select, 4);
+ data.quota_id = sqlite3_column_int(
+ datausage_restriction_select, 5);
+ data.roaming = sqlite3_column_int(
+ datausage_restriction_select, 6);
+ data.imsi = (char *)sqlite3_column_text(
+ datausage_restriction_select, 7);
+
+ if (restriction_cb(&data, user_data) == RESOURCED_CANCEL)
+ rc = SQLITE_DONE;
+ break;
+ case SQLITE_ERROR:
+ default:
+ _E("Failed to enumerate restrictions: %s\n",
+ sqlite3_errmsg(resourced_get_database()));
+
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ }
+ } while (rc == SQLITE_ROW);
+
+ sqlite3_reset(datausage_restriction_select);
+ return error_code;
+}
+
+API resourced_ret_c set_net_restriction(const char *app_id,
+ const resourced_net_restrictions *rst)
+{
+ if (!app_id || !rst)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ if (rst->imsi == NULL)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ return process_restriction(app_id, rst, RST_SET);
+}
+
+API resourced_ret_c remove_restriction(const char *app_id)
+{
+ return remove_restriction_by_iftype(app_id, RESOURCED_IFACE_ALL);
+}
+
+API resourced_ret_c remove_restriction_full(const char *app_id,
+ const resourced_net_restrictions *rst)
+{
+ return process_restriction(app_id, rst, RST_UNSET);
+}
+
+API resourced_ret_c remove_restriction_by_iftype(
+ const char *app_id, const resourced_iface_type iftype)
+{
+ resourced_net_restrictions rst = { 0 };
+
+ rst.iftype = iftype;
+ return process_restriction(app_id, &rst, RST_UNSET);
+}
+
+API resourced_ret_c resourced_remove_restriction(const char *app_id, char *imsi)
+{
+ return resourced_remove_restriction_by_iftype(app_id, RESOURCED_IFACE_ALL, imsi);
+}
+
+API resourced_ret_c resourced_remove_restriction_by_iftype(
+ const char *app_id, const resourced_iface_type iftype, char *imsi)
+{
+ resourced_net_restrictions rst = { 0 };
+
+ if (!app_id || !imsi)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ rst.iftype = iftype;
+ rst.imsi = imsi;
+ rst.rs_type = RESOURCED_STATE_BACKGROUND;
+ return process_restriction(app_id, &rst, RST_UNSET);
+}
+
+API resourced_ret_c exclude_restriction(const char *app_id)
+{
+ return exclude_restriction_by_iftype(app_id, RESOURCED_IFACE_ALL);
+}
+
+API resourced_ret_c exclude_restriction_by_iftype(
+ const char *app_id, const resourced_iface_type iftype)
+{
+ resourced_net_restrictions rst = { 0 };
+
+ rst.iftype = iftype;
+ rst.rs_type = RESOURCED_STATE_BACKGROUND;
+ return process_restriction(app_id, &rst, RST_EXCLUDE);
+}
+
+API resourced_ret_c set_net_exclusion(const char *app_id,
+ const resourced_net_restrictions *rst)
+{
+ if (!app_id || !rst)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ if (rst->imsi == NULL)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ return process_restriction(app_id, rst, RST_EXCLUDE);
+}
+
+void finalize_datausage_restriction(void)
+{
+ if (datausage_restriction_select) {
+ sqlite3_finalize(datausage_restriction_select);
+ datausage_restriction_select = NULL;
+ }
+}
+
+static int init_get_rst_statement(sqlite3* db)
+{
+ int rc;
+
+ rc = sqlite3_prepare_v2(db, SELECT_RESTRICTION_STATE, -1 ,
+ &restriction_get_state_stmt, NULL);
+ if (rc != SQLITE_OK) {
+ _E("can not prepare restriction_get_state: %d\n", rc);
+ restriction_get_state_stmt = NULL;
+ return NETWORK_ERROR_DB_FAILED;
+ }
+ return rc;
+}
+
+API resourced_ret_c get_restriction_state(const char *pkg_id,
+ resourced_iface_type iftype, resourced_restriction_state *state)
+{
+
+ int error_code = RESOURCED_ERROR_NONE;
+ sqlite3 *db;
+
+ if (state == NULL) {
+ _E("Please provide valid argument!");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ db = resourced_get_database();
+
+ if (db == NULL) {
+ _E("Can't get database.");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ execute_once {
+ error_code = init_get_rst_statement(db);
+ if (error_code != RESOURCED_ERROR_NONE)
+ return error_code;
+ }
+
+ *state = RESOURCED_RESTRICTION_UNKNOWN;
+ sqlite3_reset(restriction_get_state_stmt);
+ DB_ACTION(sqlite3_bind_text(restriction_get_state_stmt, 1, pkg_id, -1,
+ SQLITE_STATIC));
+ DB_ACTION(sqlite3_bind_int(restriction_get_state_stmt, 2, iftype));
+
+ error_code = sqlite3_step(restriction_get_state_stmt);
+ switch (error_code) {
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ROW:
+ *state = (network_restriction_state)sqlite3_column_int(
+ restriction_get_state_stmt, 0);
+ break;
+ case SQLITE_ERROR:
+ default:
+ _E("Can't perform sql query: %s \n%s",
+ SELECT_RESTRICTION_STATE, sqlite3_errmsg(db));
+ error_code = RESOURCED_ERROR_DB_FAILED;
+ }
+
+handle_error:
+
+ sqlite3_reset(restriction_get_state_stmt);
+ return error_code;
+}
+
--- /dev/null
+/*
+ * 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 settings.c
+ *
+ * @desc Entity for load vconf options
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <vconf/vconf.h>
+
+#include "macro.h"
+#include "settings.h"
+#include "trace.h"
+
+API int load_vconf_net_options(resourced_net_options *options)
+{
+ int val = 0;
+
+ if (!options) {
+ _E("Please provide valid argument!");
+ return -1;
+ }
+ if (vconf_get_bool(RESOURCED_WIFI_STATISTICS_PATH, &val) == 0)
+ options->wifi = val ?
+ RESOURCED_OPTION_ENABLE : RESOURCED_OPTION_DISABLE;
+ else {
+ _D("Can not get WiFi statistics settings");
+ return -1;
+ }
+
+ if (vconf_get_bool(RESOURCED_DATACALL_PATH, &val) == 0)
+ options->datacall = val ?
+ RESOURCED_OPTION_ENABLE : RESOURCED_OPTION_DISABLE;
+ else {
+ _D("Can not get DataCall settings");
+ return -1;
+ }
+
+ if (vconf_get_bool(RESOURCED_DATACALL_LOGGING_PATH, &val) == 0)
+ options->datacall_logging = val ?
+ RESOURCED_OPTION_ENABLE : RESOURCED_OPTION_DISABLE;
+ else {
+ _D("Can not get DataCall logging settings");
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * 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 specific-trace.c
+ *
+ * @desc functions for tracing complex entities
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "specific-trace.h"
+#include "macro.h"
+#include "trace.h"
+
+gboolean print_appstat(gpointer key, gpointer value,
+ void __attribute__((__unused__)) *data)
+{
+ struct application_stat *appstat = (struct application_stat *)value;
+ struct classid_iftype_key *composite_key =
+ (struct classid_iftype_key *)key;
+
+ if (!appstat || !composite_key) {
+ _E("Please provide valid argument for printing app stat\n");
+ return TRUE; /*stop printing*/
+ }
+
+ _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->ground);
+
+ return FALSE;
+}
--- /dev/null
+/*
+ * 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 storage.c
+ *
+ * @desc Entity for storing applications statistics
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <glib.h>
+#include <inttypes.h>
+#include <sqlite3.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "const.h"
+#include "database.h"
+#include "datausage-quota-processing.h"
+#include "db-guard.h"
+#include "iface.h"
+#include "macro.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;
+
+enum { read_until_null = -1 };
+
+static void handle_on_iface(const int ifindex, resourced_option_state state)
+{
+ resourced_iface_type iftype;
+ if (!update_iface_query) {
+ _E("Uninitialized statement");
+ return;
+ }
+ iftype = get_iftype(ifindex);
+ _D("Handling network interface ifindex:%d, state: %d, iftype: %d",
+ ifindex, state, iftype);
+ if (sqlite3_bind_int(update_iface_query, 1, iftype)
+ != SQLITE_OK) {
+ _E("Can not bind iftype:%d for preparing statement",
+ iftype);
+ return;
+ }
+ if (sqlite3_bind_int(update_iface_query, 2, state)
+ != SQLITE_OK) {
+ _E("Can not bind state:%d for preparing statement",
+ state);
+ return;
+ }
+ if (sqlite3_step(update_iface_query) != SQLITE_DONE)
+ _E("Failed to record iface state. %s",
+ sqlite3_errmsg(resourced_get_database()));
+
+ sqlite3_reset(update_iface_query);
+}
+
+static void handle_on_iface_up(const int ifindex)
+{
+ handle_on_iface(ifindex, RESOURCED_OPTION_ENABLE);
+}
+
+static void handle_on_iface_down(const int ifindex)
+{
+ handle_on_iface(ifindex, RESOURCED_OPTION_DISABLE);
+}
+
+static int init_update_statistics_query(sqlite3 *db)
+{
+ int rc;
+
+ if (update_statistics_query)
+ return SQLITE_OK;
+
+ rc = sqlite3_prepare_v2(db,
+ "insert into statistics " \
+ "(binpath, received, sent, time_stamp, " \
+ "iftype, is_roaming, hw_net_protocol_type, " \
+ "ifname, imsi, ground) " \
+ "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ read_until_null, &update_statistics_query, NULL);
+
+ if (rc != SQLITE_OK) {
+ _E("Failed to prepare query %s\n", sqlite3_errmsg(db));
+ sqlite3_finalize(update_statistics_query);
+ }
+ return rc;
+}
+
+static int init_update_iface_query(sqlite3 *db)
+{
+ int rc;
+
+ if (update_iface_query)
+ return SQLITE_OK;
+
+ rc = sqlite3_prepare_v2(db,
+ "insert into iface_status " \
+ "(update_time, iftype, ifstatus) " \
+ "values (datetime('now'), ?, ?)", read_until_null,
+ &update_iface_query, NULL);
+
+ if (rc != SQLITE_OK) {
+ _E("Failed to prepare query %s\n", sqlite3_errmsg(db));
+ sqlite3_finalize(update_iface_query);
+ }
+ return rc;
+}
+
+static gboolean store_application_stat(gpointer key, gpointer value,
+ gpointer __attribute((__unused__)) userdata)
+{
+ struct application_stat *stat = (struct application_stat *)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_current_protocol(stat_key->iftype);
+
+ if (!update_statistics_query) {
+ _E("Uninitialized statement");
+ return FALSE;
+ }
+
+ if (!stat->rcv_count && !stat->snd_count)
+ return FALSE;
+
+ if (sqlite3_bind_text(update_statistics_query, 1, stat->application_id, read_until_null,
+ SQLITE_STATIC) != SQLITE_OK) {
+ _SE("Can not bind application_id: %s", stat->application_id);
+ return FALSE;
+ }
+ if (sqlite3_bind_int(update_statistics_query, 2, stat->rcv_count) != SQLITE_OK) {
+ _E("Can not bind rcv_count %d:", stat->rcv_count);
+ return FALSE;
+ }
+ if (sqlite3_bind_int(update_statistics_query, 3, stat->snd_count) != SQLITE_OK) {
+ _E("Can not bind snd_count: %d", stat->snd_count);
+ return FALSE;
+ }
+ if (sqlite3_bind_int64(update_statistics_query, 4, (sqlite3_int64) (*last_touch_time)) !=
+ SQLITE_OK) {
+ _E("Can not bind last_touch_time: %ld", *last_touch_time);
+ return FALSE;
+ }
+ if (sqlite3_bind_int(update_statistics_query, 5, (int)(stat_key->iftype)) != SQLITE_OK) {
+ _E("Can not bind iftype: %d", (int)stat_key->iftype);
+ return FALSE;
+ }
+ if (sqlite3_bind_int(update_statistics_query, 6, (int)(stat->is_roaming)) != SQLITE_OK) {
+ _E("Can not bind is_roaming: %d", (int)(stat->is_roaming));
+ return FALSE;
+ }
+ if (sqlite3_bind_int(update_statistics_query, 7,
+ (int)hw_net_protocol_type) != SQLITE_OK) {
+ _E("Can not bind protocol_type: %d", (int)hw_net_protocol_type);
+ return FALSE;
+ }
+ if (sqlite3_bind_text(update_statistics_query, 8, stat_key->ifname, read_until_null,
+ SQLITE_STATIC) != SQLITE_OK) {
+ _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;
+ stat->snd_count = 0;
+ if (sqlite3_step(update_statistics_query) != SQLITE_DONE)
+ _E("Failed to record appstat. %s", sqlite3_errmsg(resourced_get_database()));
+
+ sqlite3_reset(update_statistics_query);
+ return FALSE;
+}
+
+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));
+
+ return RESOURCED_ERROR_NONE;
+}
+
+void finalize_storage_stm(void)
+{
+ sqlite3_finalize(update_statistics_query);
+ sqlite3_finalize(update_iface_query);
+}
+
+iface_callback *create_iface_storage_callback(void)
+{
+ iface_callback *ret_arg =
+ (iface_callback *)malloc(sizeof(iface_callback));
+
+ if (init_update_iface_query(resourced_get_database())
+ != SQLITE_OK) {
+ _E("Initialization database failed\n");
+ }
+ ret_value_msg_if(!ret_arg, NULL, "Malloc of iface_callback failed\n");
+ ret_arg->handle_iface_up = handle_on_iface_up;
+ ret_arg->handle_iface_down = handle_on_iface_down;
+
+ return ret_arg;
+}
--- /dev/null
+/*
+ * 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 <system_info.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"
+
+#define ROAMING_FEATURE "http://developer.samsung.com/tizen/feature/data_usage.roaming"
+
+/**
+ * @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 NETWORK_DEBUG_ENABLED
+#define _D_DBUS _D
+#else
+#define _D_DBUS(s, args...) ({ do { } while (0); })
+#endif /*NETWORK_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 current_sim;
+ }
+
+ 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));
+ if (!state) {
+ _E("Out of memory.");
+ return;
+ }
+ 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 = strndup(name, strlen(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++)
+ snprintf(modem->imsi_hash + (i * 2), 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);
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+ _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);
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+ _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);
+ dbus_message_unref(msg);
+ dbus_error_free(&err);
+ _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;
+ size_t path_size = sizeof(DBUS_TELEPHONY_DEFAULT_PATH) + strlen(modem->name) + 2;
+
+ modem->path = (char *)malloc(path_size);
+ if (!modem->path) {
+ _E("Out of memory, malloc failed");
+ return;
+ }
+ snprintf(modem->path, path_size, "%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);
+ free(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;
+ size_t path_size = sizeof(DBUS_TELEPHONY_DEFAULT_PATH) + strlen(modem->name) + 2;
+
+ modem->path = (char *)malloc(path_size);
+ if (!modem->path) {
+ _E("Out of memory");
+ return;
+ }
+ snprintf(modem->path, path_size, "%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);
+ free(modem->path);
+ modem->path = NULL;
+ continue;
+ }
+ }
+}
+
+static void init_telephony(void)
+{
+ if (!modems) {
+ 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)
+{
+ static bool system_info_read = false;
+ static bool roaming = false;
+
+ /* Just read roaming condition once */
+ if (!system_info_read) {
+ int ret = system_info_get_custom_bool(ROAMING_FEATURE, &roaming);
+ if (ret != SYSTEM_INFO_ERROR_NONE)
+ _E("get %s failed!!!, ret: %d, roaming %d", ROAMING_FEATURE, ret, (int)roaming);
+ system_info_read = true;
+ }
+
+ if (roaming) {
+ init_telephony(); /* one time lazy initialization */
+ ret_value_msg_if(!current_modem, RESOURCED_ROAMING_UNKNOWN,
+ "There is no current modem!");
+ if (current_modem->roaming)
+ return RESOURCED_ROAMING_ENABLE;
+ }
+
+ return 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);
+
+}
+
--- /dev/null
+/*
+ * 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 tethering-restriction.c
+ *
+ * @desc Implementation of tethering restriction for tethering pseudo app
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "resourced.h"
+#include "tethering-restriction.h"
+#include "file-helper.h"
+
+resourced_ret_c apply_tethering_restriction(
+ const enum traffic_restriction_type type)
+{
+ _D("apply tethering rule %d", type);
+ switch (type) {
+ case RST_SET:
+ return fwrite_str(PATH_TO_PROC_IP_FORWARD, "0");
+ case RST_UNSET:
+ return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1");
+ case RST_EXCLUDE:
+ return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1");
+ default:
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ }
+ return RESOURCED_ERROR_NONE;
+}
--- /dev/null
+/*
+ * 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 update.c
+ *
+ * @desc Implementation of API network statistics update
+ *
+ */
+
+
+#include "resourced.h"
+
+#include "const.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "trace.h"
+
+static E_DBus_Signal_Handler *handler;
+static E_DBus_Connection *edbus_conn;
+
+static dbus_bool_t dbus_call_method(const char *dest, const char *path,
+ const char *interface, const char *method)
+{
+ DBusConnection *conn;
+ DBusMessage *msg;
+ dbus_bool_t ret;
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ _E("dbus_bus_get failed\n");
+ return FALSE;
+ }
+
+ msg = dbus_message_new_method_call(dest, path, interface, method);
+ if (!msg) {
+ _E("Create dbus message failed\n");
+ return FALSE;
+ }
+
+ ret = dbus_connection_send(conn, msg, NULL);
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(msg);
+ dbus_connection_unref(conn);
+ return ret;
+}
+
+API resourced_ret_c resourced_update_statistics(void)
+{
+ dbus_bool_t ret = dbus_call_method(RESOURCED_DBUS_BUS_NAME,
+ RESOURCED_PATH_NETWORK,
+ RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_UPDATE);
+ if (ret == FALSE) {
+ _D("Error resourced update statistics\n");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+struct update_context {
+ void *user_data;
+ network_update_cb cb;
+};
+
+static void network_update_dbus_handler(void *user_data, DBusMessage *msg)
+{
+ struct update_context *context;
+ struct network_update_info info;
+
+ ret_msg_if(user_data == NULL,
+ "Not valid user data");
+ context = user_data;
+ ret_msg_if(context->cb == NULL,
+ "Not valid user data");
+
+ if (context->cb(&info, context->user_data) == NETWORK_CANCEL) {
+ network_unregister_update_cb();
+ }
+}
+
+API network_error_e network_register_update_cb(network_update_cb update_cb,
+ void *user_data)
+{
+ static int edbus_init_val;
+ static struct update_context context;
+
+ ret_value_msg_if(update_cb == NULL, NETWORK_ERROR_INVALID_PARAMETER,
+ "Please provide valid callback argument!");
+
+ ret_value_msg_if(handler != NULL, NETWORK_ERROR_INVALID_PARAMETER,
+ "Only one callback is supported!");
+
+ context.user_data = user_data;
+ context.cb = update_cb;
+
+ edbus_init_val = e_dbus_init();
+ ret_value_msg_if(edbus_init_val == 0,
+ NETWORK_ERROR_FAIL, "Fail to initialize dbus!");
+
+ edbus_conn = e_dbus_bus_get(DBUS_BUS_SYSTEM);
+ if (edbus_conn == NULL)
+ goto dbus_release;
+
+ handler = e_dbus_signal_handler_add(edbus_conn, NULL,
+ RESOURCED_PATH_NETWORK,
+ RESOURCED_INTERFACE_NETWORK,
+ RESOURCED_NETWORK_UPDATE_FINISH,
+ network_update_dbus_handler, &context);
+
+ if (handler == NULL)
+ goto dbus_close;
+
+ return NETWORK_ERROR_NONE;
+dbus_close:
+ e_dbus_connection_close(edbus_conn);
+
+dbus_release:
+ e_dbus_shutdown();
+ return NETWORK_ERROR_FAIL;
+}
+
+API void network_unregister_update_cb(void)
+{
+ e_dbus_signal_handler_del(edbus_conn, handler);
+ e_dbus_connection_close(edbus_conn);
+ e_dbus_shutdown();
+
+ handler = NULL;
+ edbus_conn = NULL;
+}
+
--- /dev/null
+SET(CMAKE_INSTALL_PREFIX /usr)
+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 ${REQUIRES_LIST})
+
+FOREACH(flag ${pkgs_proc_stat_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC")
+
+IF("${ARCH}" STREQUAL "arm")
+ ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+SET(SOURCES
+ ${PROC-STAT_SOURCE_DIR}/proc-stat.c
+ ${COMMON_SOURCE_DIR}/procfs.c)
+
+ADD_LIBRARY(${PROC-STAT} SHARED ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROC-STAT} ${pkgs_proc_stat_LDFLAGS} resourced_shared)
+
+SET_TARGET_PROPERTIES(${PROC-STAT}
+ PROPERTIES
+ VERSION ${FULLVER}
+ SOVERSION ${MAJORVER}
+ CLEAN_DIRECT_OUTPUT 1
+)
+
+
+INSTALL(TARGETS ${PROC-STAT} DESTINATION lib)
+INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/proc_stat.h DESTINATION include/system)
+
+IF(UNIX)
+
+ADD_CUSTOM_TARGET (distclean @echo cleaning for source distribution)
+ADD_CUSTOM_COMMAND(
+ DEPENDS clean
+ COMMENT "distribution clean"
+ COMMAND find
+ ARGS .
+ -not -name config.cmake -and \(
+ -name CMakeFiles -or
+ -name cmake.depends -or
+ -name cmake.check_depends -or
+ -name CMakeCache.txt -or
+ -name cmake.check_cache -or
+ -name *.cmake -or
+ -name Makefile -or
+ -name core -or
+ -name core.* -or
+ -name gmon.out -or
+ -name install_manifest.txt -or
+ -name *.pc -or
+ -name *~ \)
+ | grep -v TC | xargs rm -rf
+ TARGET distclean
+ VERBATIM
+)
+
+ENDIF(UNIX)
--- /dev/null
+/*
+ * 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-appusage.h
+ * @desc check history (application frequency and application execution time) based application usage
+ **/
+
+#ifndef __PROC_APPUSAGE_H__
+#define __PROC_APPUSAGE_H__
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif /* !__cplusplus */
+
+bool proc_check_favorite_app(char *appid);
+
+#endif /*__PROC_APPUSAGE_H__*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc-handler.h
+ * @desc proc handler
+ **/
+
+#ifndef __PROC_HANDLER_H__
+#define __PROC_HANDLER_H__
+
+#define PREDEF_FOREGRD "foregrd"
+#define PREDEF_BACKGRD "backgrd"
+
+void dbus_proc_handler(char* type, char *buf);
+
+#endif
--- /dev/null
+/*
+ * 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-info.h
+ * @desc communication api with libresourced for system & process information
+ **/
+
+#ifndef __PROC_INFO_H__
+#define __PROC_INFO_H__
+
+#define RESOURCED_PROC_INFO_SOCKET_PATH "/tmp/proc_info"
+
+#endif /*__PROC_HANDLER_H__*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc-main.h
+ * @desc intialize and start pthread for lowmem handler
+ **/
+
+#ifndef __PROC_MAIN_H__
+#define __PROC_MAIN_H__
+
+#include <unistd.h>
+#include <glib.h>
+#include <string.h>
+
+#include "proc-common.h"
+#include "resourced.h"
+#include "const.h"
+#include "proc_stat.h"
+
+struct proc_module_ops {
+ char *name;
+ int (*init) (void *data);
+ int (*exit) (void *data);
+};
+
+#define PROC_MODULE_REGISTER(module) \
+static void __attribute__ ((constructor)) module_init(void) \
+{ \
+ proc_module_add(module); \
+} \
+static void __attribute__ ((destructor)) module_exit(void) \
+{ \
+ proc_module_remove(module); \
+}
+
+void proc_module_add(const struct proc_module_ops *mod);
+void proc_module_remove(const struct proc_module_ops *mod);
+
+/**
+ * @desc This function handle PROC_ typs @see
+ */
+int resourced_proc_action(int type, int argnum, char **arg);
+
+int resourced_proc_excluded(const char *app_name);
+
+int resourced_proc_status_change(int status, pid_t pid, char* app_name, char* pkg_name, int apptype);
+
+void resourced_proc_dump(int type, const char *path);
+
+resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type);
+
+struct proc_app_info *proc_create_app_list(const char *appid, const char *pkgid);
+
+int proc_remove_app_list(const pid_t pid);
+
+char *proc_get_appid_from_pid(const pid_t pid);
+
+void proc_set_group(pid_t onwerpid, pid_t childpid, char *pkgname);
+
+static inline gint compare_pid(gconstpointer a, gconstpointer b)
+{
+ const struct child_pid *pida = (struct child_pid *)a;
+ const struct child_pid *pidb = (struct child_pid *)b;
+ return pida->pid == pidb->pid ? 0 :
+ pida->pid > pidb->pid ? 1 : -1;
+}
+
+int proc_get_state(int type, pid_t pid, char *buf, int len);
+
+#endif /*__PROC_MAIN_H__ */
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc-monitor.h
+ * @desc proc monitor
+ **/
+
+#ifndef __RESOURCED_PROC_MONITOR_H__
+#define __RESOURCED_PROC_MONITOR_H__
+
+#include <resourced.h>
+
+/*
+ * Initialize proc monitor module by registering it in edbus.
+ */
+
+enum proc_watchdog_type {
+ PROC_WATCHDOG_DISABLE,
+ PROC_WATCHDOG_ENABLE,
+};
+
+enum proc_dbus_use_type { /** cgroup command type **/
+ PROC_DBUS_DISABLE,
+ PROC_DBUS_ENABLE,
+};
+
+void proc_set_watchdog_state(int state);
+
+#endif /* __RESOURCED_PROC_MONITOR_H__ */
+
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc-noti.h
+ * @desc communication api with libresourced for grouping process
+ **/
+
+#ifndef __PROC_NOTI_H__
+#define __PROC_NOTI_H__
+
+#define RESOURCED_SOCKET_PATH "/tmp/resourced"
+#define NOTI_MAXARG 16
+#define NOTI_MAXARGLEN 256
+
+struct resourced_noti { /** cgroup notification type **/
+ int pid;
+ int type;
+ int argc;
+ char *argv[NOTI_MAXARG];
+};
+
+#define SYNC_OPERATION(type) type == PROC_CGROUP_GET_MEMSWEEP || \
+ type == PROC_CGROUP_SET_RESUME_REQUEST || \
+ type == PROC_CGROUP_SET_TERMINATE_REQUEST || \
+ type >= PROC_CGROUP_GET_CMDLINE
+
+int write_response(int *retval, int fd, char *buf, int len);
+int read_message(int fd, struct resourced_noti *msg);
+void free_message(struct resourced_noti *msg);
+
+#endif /*__PROC_HANDLER_H__*/
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc_process.h
+ * @desc grouping process and setting oom adj value
+ **/
+
+#ifndef __PROC_PROCESS_H__
+#define __PROC_PROCESS_H__
+
+enum proc_sweep_type {
+ PROC_SWEEP_EXCLUDE_ACTIVE,
+ PROC_SWEEP_INCLUDE_ACTIVE,
+};
+
+int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid);
+
+int proc_set_foregrd(int pid, int oom_score_adj);
+int proc_set_backgrd(int pid, int oom_score_adj);
+
+int proc_set_active(int pid, int oom_score_adj);
+int proc_set_inactive(int pid, int oom_score_adj);
+
+int proc_set_service_oomscore(const pid_t pid, const int oom_score);
+
+
+#endif /*__PROC_PROCESS_H__*/
--- /dev/null
+/*
+ * 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__ */
+
--- /dev/null
+/*
+ * 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-appusage.c
+ * @desc check history (application frequency and application execution time) based application usage
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <Ecore.h>
+
+#include "resourced.h"
+#include "logging.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "proc-main.h"
+#include "notifier.h"
+#include "config-parser.h"
+
+#ifdef HEART_SUPPORT
+#include "heart-common.h"
+#endif
+
+#define UPDATE_INTERVAL DAY_TO_SEC(1)
+#define APPUSAGE_CONF_FILE "/etc/resourced/proc.conf"
+#define APPUSAGE_CONF_SECTION "APPUSAGE"
+
+static int favorite_count;
+static GHashTable *appusage_favorite_htab;
+
+#ifdef HEART_SUPPORT
+static Ecore_Timer *appusage_update_timer;
+#endif
+
+static void free_key(gpointer data)
+{
+ if (data)
+ free(data);
+}
+
+static gboolean find_hash(gpointer key, gpointer value, gpointer user_data)
+{
+ return (gboolean)strstr((char *)user_data, (char *)key);
+}
+
+static void print_favorite_list(gpointer key, gpointer value, gpointer user_data)
+{
+ _D("favorit app list : %s", (char*)key);
+}
+
+#ifdef HEART_SUPPORT
+static Eina_Bool appusage_update_cb(void *data)
+{
+ GHashTable *apps_htab = (GHashTable *)data;
+ int ret;
+
+ if (!data)
+ return ECORE_CALLBACK_CANCEL;
+
+ ret = heart_cpu_get_appusage_list(apps_htab, favorite_count);
+ if (!ret) {
+ _I("most_recently_used_list updated");
+ g_hash_table_foreach(apps_htab, print_favorite_list, NULL);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+}
+#endif
+
+static int load_appusage_config(struct parse_result *result, void *user_data)
+{
+ if(!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, APPUSAGE_CONF_SECTION))
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (!strcmp(result->name, "APPUSAGE")) {
+ if (!strcmp(result->value, "OFF"))
+ return RESOURCED_ERROR_UNINITIALIZED;
+
+ appusage_favorite_htab = g_hash_table_new_full(g_str_hash,
+ g_str_equal, free_key, NULL);
+
+ } else if (!strcmp(result->name, "PREDEFINE")) {
+ g_hash_table_insert(appusage_favorite_htab,
+ g_strndup(result->value, strlen(result->value)), GINT_TO_POINTER(1));
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_appusage_table_init(void)
+{
+ int ret;
+
+ ret = config_parse(APPUSAGE_CONF_FILE, load_appusage_config, NULL);
+ if(ret || !appusage_favorite_htab)
+ return RESOURCED_ERROR_NO_DATA;
+
+ favorite_count = g_hash_table_size(appusage_favorite_htab);
+
+#ifdef HEART_SUPPORT
+ ret = heart_cpu_get_appusage_list(appusage_favorite_htab,
+ favorite_count);
+ if (!ret)
+ _I("most_recently_used_list updated");
+#endif
+
+ g_hash_table_foreach(appusage_favorite_htab, print_favorite_list, NULL);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int booting_done(void *data)
+{
+ static const struct module_ops *swap;
+ int ret;
+
+ /*
+ * When kernel enables swap feature,
+ * resourced can control favorite applications because it will
+ * apply early swap and late oom kill.
+ */
+ if (!swap) {
+ swap = find_module("swap");
+ if (!swap)
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ ret = proc_appusage_table_init();
+ if (ret)
+ return RESOURCED_ERROR_NO_DATA;
+
+#ifdef HEART_SUPPORT
+ appusage_update_timer = ecore_timer_add(UPDATE_INTERVAL,
+ appusage_update_cb, (void *)appusage_favorite_htab);
+#endif
+
+ return RESOURCED_ERROR_NONE;
+}
+
+bool proc_check_favorite_app(char *appid)
+{
+ gpointer app_ptr = NULL;
+
+ if (!appusage_favorite_htab)
+ return false;
+
+ app_ptr = g_hash_table_find(appusage_favorite_htab,
+ find_hash, (gpointer)appid);
+ if (app_ptr)
+ return true;
+ return false;
+}
+
+static int proc_appusage_init(void *data)
+{
+ register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_appusage_exit(void *data)
+{
+ if (appusage_favorite_htab)
+ g_hash_table_destroy(appusage_favorite_htab);
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct proc_module_ops proc_appusage_ops = {
+ .name = "PROC_APPUSAGE",
+ .init = proc_appusage_init,
+ .exit = proc_appusage_exit,
+};
+PROC_MODULE_REGISTER(&proc_appusage_ops)
--- /dev/null
+/*
+ * 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 proc-handler.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "trace.h"
+#include "edbus-handler.h"
+
+#define SIGNAL_NAME_OOMADJ_SET "OomadjSet"
+
+void dbus_proc_handler(char *type, char *buf)
+{
+ char *pa[3];
+ int ret;
+
+ pa[0] = type;
+ pa[1] = "1";
+ pa[2] = buf;
+
+ ret = broadcast_edbus_signal_str(DEVICED_PATH_PROCESS, DEVICED_INTERFACE_PROCESS,
+ SIGNAL_NAME_OOMADJ_SET, "sis", pa);
+ if (ret < 0)
+ _E("Fail to send dbus signal to deviced!!");
+}
--- /dev/null
+/*
+ * 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_info.c
+ * @desc It's main thread to get system & process information
+ * to provide runtime api
+*/
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <pthread.h>
+
+#include <Ecore.h>
+
+#include "macro.h"
+#include "proc-main.h"
+#include "proc-info.h"
+#include "proc-process.h"
+#include "proc-noti.h"
+#include "resourced.h"
+#include "module.h"
+#include "trace.h"
+
+#define MAX_CONNECTION 5
+
+static pthread_t proc_info_thread;
+
+static void *resourced_proc_info_func(void *data)
+{
+ struct sockaddr_un client_address;
+ struct resourced_noti *msg;
+ char *send_buffer = NULL;
+ int ret, send_len = 0;
+ int server_fd, client_fd, client_len;
+ pid_t pid;
+ struct timeval tv = { 1, 0 }; /* 1 sec */
+
+ if (!data) {
+ _E("data is NULL");
+ return NULL;
+ }
+
+ server_fd = (int)data;
+
+ client_len = sizeof(client_address);
+
+ if (listen(server_fd, MAX_CONNECTION) < 0) {
+ _E("Failed to listen socket");
+ close(server_fd);
+ return NULL;
+ }
+
+ while (1) {
+ client_fd =
+ accept(server_fd, (struct sockaddr *)&client_address,
+ (socklen_t *)&client_len);
+ if (client_fd < 0) {
+ _E("Failed to accept");
+ continue;
+ }
+ msg = calloc(1, sizeof(struct resourced_noti));
+ if (msg == NULL) {
+ _E("proc_noti_cb : Not enough memory");
+ goto end;
+ }
+ ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret)
+ _E("failed to set socket option");
+ ret = read_message(client_fd, msg);
+ if (ret)
+ goto end;
+ if (msg->argc > NOTI_MAXARG || msg->type < PROC_CGROUP_GET_CMDLINE) {
+ _E("%s : error argument", __func__);
+ goto end;
+ }
+
+ pid = atoi(msg->argv[0]);
+ send_len = atoi(msg->argv[1]);
+ send_buffer = calloc(1, send_len);
+ if (send_buffer == NULL) {
+ _E("Not enough memory");
+ goto end;
+ }
+ ret = proc_get_state(msg->type, pid, send_buffer, send_len);
+end:
+ write_response(&ret, client_fd, send_buffer, send_len);
+ free_message(msg);
+ close(client_fd);
+ if (send_buffer) {
+ free(send_buffer);
+ send_buffer = NULL;
+ }
+ ret = 0;
+ send_len = 0;
+ }
+
+ return NULL;
+}
+
+static int proc_info_socket_init(void)
+{
+ int fd;
+ struct sockaddr_un serveraddr;
+
+ if (access(RESOURCED_PROC_INFO_SOCKET_PATH, F_OK) == 0)
+ unlink(RESOURCED_PROC_INFO_SOCKET_PATH);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ _E("Failed to create socket");
+ return -1;
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPOUT", "@", 2, 0)) < 0) {
+ _E("Failed to set Socket SMACK label");
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPIN", "*", 2, 0)) < 0) {
+ _E("Failed to set Socket SMACK label");
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ bzero(&serveraddr, sizeof(struct sockaddr_un));
+ serveraddr.sun_family = AF_UNIX;
+ strncpy(serveraddr.sun_path, RESOURCED_PROC_INFO_SOCKET_PATH,
+ sizeof(serveraddr.sun_path));
+
+ if (bind(fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)) < 0) {
+ _E("Failed to bind socket");
+ close(fd);
+ return -1;
+ }
+
+ if (chmod(RESOURCED_PROC_INFO_SOCKET_PATH, (S_IRWXU | S_IRWXG | S_IRWXO)) < 0)
+ _E("Failed to change the socket permission");
+ _D("socket create ok");
+ return fd;
+}
+
+static int proc_info_init(void *data)
+{
+ int fd, ret;
+
+ fd = proc_info_socket_init();
+ if (fd < 0) {
+ _E("Failed to init socket");
+ return -1;
+ }
+ /* start thread */
+ ret = pthread_create(&proc_info_thread, NULL, resourced_proc_info_func,
+ (void *)fd);
+ if (ret != 0) {
+ _E("Failed to create thread");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_info_exit(void *data)
+{
+ _D("proc info exit!");
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct proc_module_ops proc_info_ops = {
+ .name = "PROC_INFO",
+ .init = proc_info_init,
+ .exit = proc_info_exit,
+};
+PROC_MODULE_REGISTER(&proc_info_ops)
--- /dev/null
+/*
+ * 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 proc-main.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <Ecore.h>
+#include <Ecore_File.h>
+#include <pthread.h>
+
+#include "freezer.h"
+#include "notifier.h"
+#include "proc-process.h"
+#include "proc-main.h"
+#include "cgroup.h"
+#include "proc-noti.h"
+#include "trace.h"
+#include "proc-handler.h"
+#include "proc-monitor.h"
+#include "module.h"
+#include "macro.h"
+#include "appid-helper.h"
+#include "lowmem-handler.h"
+#include "procfs.h"
+#include "appinfo-list.h"
+#include "util.h"
+
+static GHashTable *proc_exclude_list;
+static Ecore_File_Monitor *exclude_list_monitor;
+static const unsigned int exclude_list_limit = 1024;
+static const struct module_ops *freezer;
+static GSList *proc_module; /* proc sub-module list */
+
+#define BASE_UGPATH_PREFIX "/usr/ug/bin"
+#define LOG_PREFIX "resourced"
+#define TIZEN_SYSTEM_APPID "org.tizen.system"
+
+GSList *proc_app_list;
+GSList *proc_program_list;
+
+struct child_pid *new_pid_info(const pid_t pid)
+{
+ struct child_pid *result = (struct child_pid *)malloc(
+ sizeof(struct child_pid));
+ if (!result) {
+ _E("Malloc of new_pid_info failed\n");
+ return NULL;
+ }
+
+ result->pid = pid;
+ return result;
+}
+
+static struct child_pid *find_child_info(pid_list pids, const pid_t pid)
+{
+ struct child_pid pid_to_find = {
+ .pid = pid,
+ };
+ GSList *found = NULL;
+
+ ret_value_msg_if(!pids, NULL, "Please provide valid pointer.");
+
+ found = g_slist_find_custom((GSList *)pids,
+ &pid_to_find, compare_pid);
+
+ if (found)
+ return (struct child_pid *)(found->data);
+ return NULL;
+}
+
+static bool is_ui_app(enum application_type type)
+{
+ if (type == PROC_TYPE_GUI || type == PROC_TYPE_WIDGET ||
+ type == PROC_TYPE_WATCH)
+ return true;
+ return false;
+}
+
+void proc_add_child_pid(struct proc_app_info *pai, pid_t pid)
+{
+ struct child_pid pid_to_find = {
+ .pid = pid,
+ };
+ GSList *found = NULL;
+
+ if (pai->childs)
+ found = g_slist_find_custom((GSList *)pai->childs,
+ &pid_to_find, compare_pid);
+
+ if (found)
+ return;
+
+ pai->childs = g_slist_prepend(pai->childs, new_pid_info(pid));
+}
+
+void proc_set_process_memory_state(struct proc_app_info *pai,
+ int memcg_idx, struct memcg_info *memcg_info, int oom_score_adj)
+{
+ if (!pai)
+ return;
+
+ pai->memory.memcg_idx= memcg_idx;
+ pai->memory.memcg_info = memcg_info;
+ pai->memory.oom_score_adj = oom_score_adj;
+}
+
+/*
+ * There can be many processes with same appid at same time.
+ * This function returns the most recently used app of all app list.
+ */
+struct proc_app_info *find_app_info_by_appid(const char *appid)
+{
+ GSList *iter = NULL;
+ struct proc_app_info *pai;
+
+ if (!appid)
+ return NULL;
+
+ gslist_for_each_item(iter, proc_app_list) {
+ pai = (struct proc_app_info *)iter->data;
+ if (equal_name_info(pai->appid, appid))
+ return pai;
+ }
+ return NULL;
+}
+
+struct proc_app_info *find_app_info(const pid_t pid)
+{
+ GSList *iter = NULL;
+ struct proc_app_info *pai= NULL;
+
+ gslist_for_each_item(iter, proc_app_list) {
+ pai = (struct proc_app_info *)iter->data;
+ if ((pai->main_pid == pid) ||
+ (pai->childs && find_child_info(pai->childs, pid)))
+ return pai;
+ }
+ return NULL;
+}
+
+struct proc_program_info *find_program_info(const char *pkgname)
+{
+ GSList *iter = NULL;
+ struct proc_program_info *ppi;
+
+ if (!pkgname)
+ return NULL;
+
+ gslist_for_each_item(iter, proc_program_list) {
+ ppi = (struct proc_program_info *)iter->data;
+ if (equal_name_info(ppi->pkgname, pkgname))
+ return ppi;
+ }
+ return NULL;
+}
+
+resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type)
+{
+ struct proc_app_info *pai = NULL;
+ struct proc_status ps = {0};
+
+ pai = find_app_info(pid);
+ if (!pai)
+ return RESOURCED_ERROR_NO_DATA;
+
+ ps.pid = pid;
+ ps.pai = pai;
+ if (type == PROC_EXCLUDE)
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+
+ if (pai->runtime_exclude) {
+ if (type == PROC_EXCLUDE)
+ pai->runtime_exclude++;
+ else
+ pai->runtime_exclude--;
+ } else {
+ pai->runtime_exclude = type;
+ }
+ _D("pid %d set proc exclude list, type = %d, exclude = %d",
+ pid, type, pai->runtime_exclude);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/*
+ * find main oom score value from latest launched UI application
+ * And set oom score of service app
+ */
+static void proc_set_default_svc_oomscore
+ (struct proc_program_info *ppi, struct proc_app_info *svc)
+{
+ struct proc_app_info *pai =
+ (struct proc_app_info *)g_slist_nth_data(ppi->app_list, 0);
+ int oom_score_adj = 0, ret ;
+ if (pai) {
+ if (CHECK_BIT(pai->flags, PROC_VIP_ATTRIBUTE))
+ oom_score_adj = OOMADJ_SU;
+ else {
+ ret = proc_get_oom_score_adj(pai->main_pid, &oom_score_adj);
+ if (ret)
+ oom_score_adj = 0;
+ }
+ }
+ proc_set_service_oomscore(svc->main_pid, oom_score_adj);
+}
+
+struct proc_program_info *proc_add_program_list(const int type,
+ struct proc_app_info *pai, const char *pkgname)
+{
+ struct proc_program_info *ppi;
+ if (!pai || !pkgname)
+ return NULL;
+
+ ppi = find_program_info(pkgname);
+ if (!ppi) {
+ _E("not found ppi : %s", pkgname);
+ ppi = calloc(sizeof(struct proc_program_info), 1);
+ if (!ppi)
+ return NULL;
+
+ if (pai->ai)
+ ppi->pkgname = pai->ai->pkgname;
+ else {
+ ppi->pkgname = strndup(pkgname, strlen(pkgname)+1);
+ if (!ppi->pkgname) {
+ _E("not enough memory");
+ free(ppi);
+ return NULL;
+ }
+ }
+ proc_program_list = g_slist_prepend(proc_program_list, ppi);
+ }
+ if (is_ui_app(type))
+ ppi->app_list = g_slist_prepend(ppi->app_list, pai);
+ else {
+ ppi->svc_list = g_slist_prepend(ppi->svc_list, pai);
+ proc_set_default_svc_oomscore(ppi, pai);
+ }
+ return ppi;
+}
+
+struct proc_app_info *proc_add_app_list(const int type, const pid_t pid,
+ const char *appid, const char *pkgname)
+{
+ struct proc_app_info *pai;
+
+ if (!appid)
+ return NULL;
+
+ /*
+ * check lastet item firstly because app list has already created in prelaunch
+ */
+ pai = (struct proc_app_info *)g_slist_nth_data(proc_app_list, 0);
+ if (!pai || pai->type != PROC_TYPE_READY) {
+ _E("not found previous pai : %s", appid);
+ pai = proc_create_app_list(appid, pkgname);
+ if (!pai) {
+ _E("failed to create app list");
+ return NULL;
+ }
+ }
+
+ pai->type = type;
+ pai->main_pid = pid;
+ pai->program = proc_add_program_list(type, pai, pkgname);
+ pai->state = PROC_STATE_FOREGROUND;
+ return pai;
+}
+
+static void _remove_child_pids(struct proc_app_info *pai, pid_t pid)
+{
+ GSList *iter, *next;
+ struct child_pid *child;
+
+ if (!pai->childs)
+ return;
+
+ /*
+ * if pid has a valid value, remove only one child with same pid
+ * otherwise pid is zero, remove all child pids
+ */
+ gslist_for_each_safe(pai->childs, iter, next, child) {
+ if (pid && pid != child->pid)
+ continue;
+ pai->childs = g_slist_remove(pai->childs, child);
+ free(child);
+ if (pid)
+ return;
+ }
+}
+
+int proc_remove_app_list(const pid_t pid)
+{
+ GSList *iter;
+ struct proc_app_info *pai = NULL;
+ struct proc_program_info *ppi;
+ struct child_pid *found = NULL;
+
+ gslist_for_each_item(iter, proc_app_list) {
+ pai = (struct proc_app_info *)iter->data;
+ if (!pai->main_pid)
+ continue;
+
+ if (pai->main_pid == pid) {
+ _remove_child_pids(pai, 0);
+ ppi = pai->program;
+ if (ppi) {
+ if (is_ui_app(pai->type))
+ ppi->app_list = g_slist_remove(ppi->app_list, pai);
+ else if (pai->type == PROC_TYPE_SERVICE)
+ ppi->svc_list = g_slist_remove(ppi->svc_list, pai);
+ if (!ppi->app_list && !ppi->svc_list) {
+ proc_program_list = g_slist_remove(proc_program_list, ppi);
+ resourced_appinfo_put(pai->ai);
+ free(ppi);
+ }
+ }
+ proc_app_list = g_slist_remove(proc_app_list, pai);
+ free(pai);
+ break;
+ } else if (pai->childs) {
+ found = find_child_info(pai->childs, pid);
+ if (!found)
+ continue;
+ _remove_child_pids(pai, pid);
+ break;
+ } else
+ continue;
+ }
+ return 0;
+}
+
+struct proc_app_info *proc_create_app_list(const char *appid, const char *pkgid)
+{
+ struct proc_app_info *pai;
+ if (!appid)
+ return NULL;
+
+ pai = calloc(sizeof(struct proc_app_info), 1);
+ if (!pai)
+ return NULL;
+
+ pai->ai = resourced_appinfo_get(pai->ai, appid, pkgid);
+ if (pai->ai)
+ pai->appid = pai->ai->appid;
+ else {
+ pai->appid = strndup(appid, strlen(appid)+1);
+ if (!pai->appid) {
+ free(pai);
+ _E("not enough memory");
+ return NULL;
+ }
+ }
+
+ pai->proc_exclude = resourced_proc_excluded(appid);
+ proc_app_list = g_slist_prepend(proc_app_list, pai);
+ return pai;
+}
+
+int proc_delete_all_lists(void)
+{
+ GSList *iter, *next;
+ struct proc_app_info *pai = NULL;
+ struct proc_program_info *ppi = NULL;
+
+ gslist_for_each_safe(proc_app_list, iter, next, pai) {
+ _remove_child_pids(pai, 0);
+ ppi = pai->program;
+ if (ppi) {
+ if (is_ui_app(pai->type))
+ ppi->app_list = g_slist_remove(ppi->app_list, pai);
+ else if (pai->type == PROC_TYPE_SERVICE)
+ ppi->svc_list = g_slist_remove(ppi->svc_list, pai);
+ }
+ proc_app_list = g_slist_remove(proc_app_list, pai);
+ resourced_appinfo_put(pai->ai);
+ free(pai);
+ }
+
+ gslist_for_each_safe(proc_program_list, iter, next, ppi) {
+ proc_program_list = g_slist_remove(proc_program_list, ppi);
+ free(ppi);
+ }
+ return 0;
+}
+
+int proc_get_svc_state(struct proc_program_info *ppi)
+{
+ GSList *iter = NULL;
+ int state = PROC_STATE_DEFAULT;
+
+ if (!ppi->app_list)
+ return PROC_STATE_DEFAULT;
+
+ gslist_for_each_item(iter, ppi->app_list) {
+ struct proc_app_info *pai = (struct proc_app_info *)(iter->data);
+
+ if (pai->lru_state == PROC_FOREGROUND)
+ return PROC_STATE_FOREGROUND;
+
+ if (pai->lru_state >= PROC_BACKGROUND)
+ state = PROC_STATE_BACKGROUND;
+ }
+ return state;
+}
+
+static void proc_dump_process_list(FILE *fp)
+{
+ GSList *iter, *iter_child;
+ struct proc_app_info *pai = NULL;
+ int index = 0, ret, oom_score_adj;
+
+ LOG_DUMP(fp, "[APPLICATION LISTS]\n");
+ gslist_for_each_item(iter, proc_app_list) {
+ char *typestr;
+ unsigned int size;
+ unsigned long utime, stime;
+
+ pai = (struct proc_app_info *)iter->data;
+ ret = proc_get_oom_score_adj(pai->main_pid, &oom_score_adj);
+ if (ret < 0)
+ continue;
+
+ if (!pai->ai)
+ continue;
+
+ if (is_ui_app(pai->type))
+ typestr = "UI APP";
+ else if (pai->type == PROC_TYPE_SERVICE)
+ typestr = "SVC APP";
+ else
+ continue;
+
+ LOG_DUMP(fp, "index : %d, type : %s, pkgname : %s, appid : %s\n"
+ "\t lru : %d, proc_exclude : %d, runtime_exclude : %d, flags : %X, "
+ "state : %d\n", index, typestr, pai->ai->pkgname, pai->ai->appid,
+ pai->lru_state, pai->proc_exclude, pai->runtime_exclude,
+ pai->flags, pai->state);
+
+ proc_get_mem_usage(pai->main_pid, NULL, &size);
+ proc_get_cpu_time(pai->main_pid, &utime, &stime);
+ LOG_DUMP(fp, "\t main pid : %d, oom score : %d, memory rss : %d KB,"
+ "utime : %lu, stime : %lu\n", pai->main_pid, oom_score_adj, size,
+ utime, stime);
+ if (pai->childs) {
+ struct child_pid *child;
+ gslist_for_each_item(iter_child, pai->childs) {
+ child = (struct child_pid *)iter_child->data;
+ proc_get_mem_usage(child->pid, NULL, &size);
+ proc_get_cpu_time(child->pid, &utime, &stime);
+ LOG_DUMP(fp, "\t main pid : %d, oom score : %d, "
+ "memory rss : %dKB, utime : %lu, stime : %lu\n",
+ pai->main_pid, oom_score_adj, size, utime, stime);
+ }
+ }
+ index++;
+ }
+}
+
+static void proc_free_exclude_key(gpointer data)
+{
+ if (data)
+ free(data);
+}
+
+static gboolean find_excluded(gpointer key, gpointer value, gpointer user_data)
+{
+ return (gboolean)strstr((char *)user_data, (char *)key);
+}
+
+int proc_get_id_info(struct proc_status *ps, char **app_name, char **pkg_name)
+{
+ if (!ps)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!ps->pai || !ps->pai->ai) {
+ *app_name = TIZEN_SYSTEM_APPID;
+ *pkg_name = TIZEN_SYSTEM_APPID;
+ } else {
+ *app_name = ps->pai->ai->appid;
+ *pkg_name = ps->pai->ai->pkgname;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+char *proc_get_appid_from_pid(const pid_t pid)
+{
+ struct proc_app_info *pai = find_app_info(pid);
+ if (!pai)
+ return NULL;
+ return pai->appid;
+}
+
+int resourced_proc_excluded(const char *app_name)
+{
+ gpointer ret = 0;
+ if (proc_exclude_list)
+ ret = g_hash_table_find(proc_exclude_list, find_excluded, (gpointer)app_name);
+ else
+ return RESOURCED_ERROR_NONE;
+ return ret ? RESOURCED_ERROR_NONMONITOR : RESOURCED_ERROR_NONE;
+}
+
+static void _prepare_appid(char *appid, const int length)
+{
+ if (!appid || length - 1 <= 0)
+ return;
+ appid[length - 1] = '\0'; /*remove ending new line*/
+}
+
+static void fill_exclude_list_by_path(const char *exclude_file_name,
+ GHashTable *list)
+{
+ char *exclude_app_id = 0;
+ int ret;
+ unsigned int excluded_count = 0;
+ size_t buf_size = 0;
+ FILE *exclude_file = NULL;
+
+ if (!list) {
+ _D("Please initialize exclude list!");
+ return;
+ }
+
+ exclude_file = fopen(exclude_file_name, "r");
+
+ if (!exclude_file) {
+ _E("Can't open %s.", exclude_file_name);
+ return;
+ }
+
+ while (excluded_count++ < exclude_list_limit) {
+ ret = getline(&exclude_app_id, &buf_size, exclude_file);
+ if (ret <= 0)
+ break;
+ _prepare_appid(exclude_app_id, ret);
+ _SD("append %s to proc exclude list", exclude_app_id);
+
+ g_hash_table_insert(list, g_strndup(exclude_app_id, strlen(exclude_app_id)),
+ GINT_TO_POINTER(1));
+ }
+
+ if (excluded_count >= exclude_list_limit)
+ _E("Exclude list is exceed the limit of %u application",
+ exclude_list_limit);
+
+ if (exclude_app_id)
+ free(exclude_app_id);
+
+ fclose(exclude_file);
+}
+
+static void _fill_exclude_list(GHashTable *list)
+{
+ fill_exclude_list_by_path(EXCLUDE_LIST_FULL_PATH, list);
+ fill_exclude_list_by_path(EXCLUDE_LIST_OPT_FULL_PATH, list);
+}
+
+static void _exclude_list_change_cb(void *data, Ecore_File_Monitor *em,
+ Ecore_File_Event event, const char *path)
+{
+ _SD("file %s changed, path: %s, event: %d ", EXCLUDE_LIST_OPT_FULL_PATH,
+ path, event);
+
+ g_hash_table_remove_all(proc_exclude_list);
+ /* reread all */
+ _fill_exclude_list(proc_exclude_list);
+}
+
+static void _init_exclude_list_noti(void)
+{
+ if (ecore_file_init() == 0) {
+ _E("ecore_file_init() failed");
+ return;
+ }
+ exclude_list_monitor = ecore_file_monitor_add(EXCLUDE_LIST_OPT_FULL_PATH,
+ _exclude_list_change_cb,
+ NULL);
+ if (exclude_list_monitor == NULL)
+ _E("Dynamic exclude list not supported. Cannot add notification callback");
+}
+
+static void proc_exclude_init(void)
+{
+ proc_exclude_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ proc_free_exclude_key,
+ NULL);
+
+ if (proc_exclude_list == NULL) {
+ _E("Can't initialize exclude_list!");
+ return;
+ }
+
+ _init_exclude_list_noti();
+ _fill_exclude_list(proc_exclude_list);
+}
+
+void proc_module_add(const struct proc_module_ops *ops)
+{
+ proc_module = g_slist_append(proc_module, (gpointer)ops);
+}
+
+void proc_module_remove(const struct proc_module_ops *ops)
+{
+ proc_module = g_slist_remove(proc_module, (gpointer)ops);
+}
+
+static void proc_module_init(void *data)
+{
+ GSList *iter;
+ const struct proc_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, proc_module) {
+ module = (struct proc_module_ops *)iter->data;
+ _D("Initialize [%s] module\n", module->name);
+ if (module->init)
+ ret = module->init(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to initialize [%s] module\n", module->name);
+ }
+}
+
+static void proc_module_exit(void *data)
+{
+ GSList *iter;
+ const struct proc_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, proc_module) {
+ module = (struct proc_module_ops *)iter->data;
+ _D("Deinitialize [%s] module\n", module->name);
+ if (module->exit)
+ ret = module->exit(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to deinitialize [%s] module\n", module->name);
+ }
+}
+
+static int resourced_proc_init(void* data)
+{
+ proc_exclude_init();
+ proc_module_init(data);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_proc_exit(void* data)
+{
+ proc_delete_all_lists();
+ g_hash_table_destroy(proc_exclude_list);
+ ecore_file_monitor_del(exclude_list_monitor);
+ proc_module_exit(data);
+ return RESOURCED_ERROR_NONE;
+}
+
+int proc_get_freezer_status()
+{
+ int ret = CGROUP_FREEZER_DISABLED;
+ struct freezer_status_data f_data;
+ if (!freezer) {
+ freezer = find_module("freezer");
+ if (!freezer)
+ return ret;
+ }
+
+ f_data.type = GET_STATUS;
+ if (freezer->status)
+ ret = freezer->status(&f_data);
+ return ret;
+}
+
+int proc_get_appflag(const pid_t pid)
+{
+ struct proc_app_info *pai =
+ find_app_info(pid);
+
+ if (pai) {
+ _D("get apptype = %d", pai->flags);
+ return pai->flags;
+ } else
+ _D("there is no process info for pid = %d", pid);
+ return PROC_NONE;
+}
+
+void proc_set_group(pid_t onwerpid, pid_t childpid, char *pkgname)
+{
+ int owner_oom = 0, child_oom = 0, ret;
+ int child_type = 0, child_state = 0;
+ struct proc_program_info *ppi;
+ struct proc_app_info *pai, *owner;
+ struct proc_status ps = {0};
+
+ if (onwerpid <= 0 || childpid <=0)
+ return;
+
+ ret = proc_get_oom_score_adj(onwerpid, &owner_oom);
+ if (ret < 0) {
+ _D("owner pid(%d) was already terminated", onwerpid);
+ return;
+ }
+
+ owner = find_app_info(onwerpid);
+ pai = find_app_info(childpid);
+ if (!owner)
+ return;
+
+ if (pkgname && pai) {
+ /*
+ * when child application with appid has owner pai and ppi
+ * check and remove them
+ */
+ if (pai->main_pid == childpid) {
+ ppi = pai->program;
+ if (ppi)
+ ppi->app_list = g_slist_remove(ppi->app_list, pai);
+
+ ret = proc_get_oom_score_adj(childpid, &child_oom);
+ if(ret < 0) {
+ _D("can't get oom score for pid (%d)", childpid);
+ child_oom = 0;
+ }
+ child_type = pai->type;
+ child_state = pai->state;
+ /*
+ * migrate application categories and activities
+ * from child pai to parent pai
+ */
+ if (pai->categories)
+ owner->categories += pai->categories;
+ if (pai->runtime_exclude)
+ owner->runtime_exclude += pai->runtime_exclude;
+ proc_app_list = g_slist_remove(proc_app_list, pai);
+ free(pai);
+ } else {
+ _D("main pid(%d) was different from childpid(%d)",
+ pai->main_pid, childpid);
+ _remove_child_pids(pai, childpid);
+ }
+ }
+ /*
+ * when some process like webprocess or
+ * UI application that has transparent with owner application
+ * needs to group in owner app
+ * and adds to child lists in the proc app info
+ */
+ proc_add_child_pid(owner, childpid);
+
+ /*
+ * When child process was GUI application and it was foreground state
+ * parent application should go to the foreground state also.
+ * Otherwise, child process follows oom and suspend state of parent application
+ */
+ if (child_type == PROC_TYPE_GUI &&
+ child_state == PROC_STATE_FOREGROUND) {
+ owner->lru_state = PROC_FOREGROUND;
+ ps.pid = owner->main_pid;
+ proc_set_oom_score_adj(owner->main_pid, child_oom);
+ } else {
+ if (owner_oom <= OOMADJ_BACKGRD_LOCKED) {
+ ps.pid = childpid;
+ ps.appid = owner->appid;
+ }
+ proc_set_oom_score_adj(childpid, owner_oom);
+ }
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+}
+
+bool proc_check_lru_suspend(int val, int lru)
+{
+ if (proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ return false;
+
+ if ((PROC_BACKGROUND + val) == lru)
+ return true;
+ return false;
+}
+
+enum proc_state proc_check_suspend_state(struct proc_app_info *pai)
+{
+ if (!pai)
+ return PROC_STATE_DEFAULT;
+
+ if (pai->type == PROC_TYPE_GUI) {
+ /*
+ * check LRU state about UI application
+ * whether it is active state or not
+ */
+ if (pai->lru_state < PROC_BACKGROUND)
+ return PROC_STATE_DEFAULT;
+
+ /*
+ * if platform has a suspend policy and application has UI,
+ * waits suspend callback or changing LRU.
+ * Otherwise, application goes to suspend state without waiting.
+ */
+ if (!(CHECK_BIT(pai->flags, PROC_BGCTRL_PLATFORM)) ||
+ (pai->state == PROC_STATE_SUSPEND_READY))
+ return PROC_STATE_SUSPEND;
+
+ pai->state = PROC_STATE_SUSPEND_READY;
+ return PROC_STATE_SUSPEND_READY;
+
+ }
+ if (pai->type == PROC_TYPE_SERVICE) {
+ /*
+ * standalone service goes to suspend state immediately.
+ * if service is connected with UI application,
+ * checks UI state from program list.
+ * if UI has already went to suspend mode,
+ * service goes to suspend state.
+ * Otherwise, service waits until UI app is suspended.
+ */
+ struct proc_program_info *ppi = pai->program;
+ struct proc_app_info *ui;
+
+ if (!ppi->app_list)
+ return PROC_STATE_SUSPEND;
+
+ ui = (struct proc_app_info *)g_slist_nth_data(ppi->app_list, 0);
+ if (ui->state == PROC_STATE_SUSPEND)
+ return PROC_STATE_SUSPEND;
+ pai->state = PROC_STATE_SUSPEND_READY;
+ return PROC_STATE_SUSPEND_READY;
+ }
+ return PROC_STATE_DEFAULT;
+}
+
+
+int resourced_proc_status_change(int status, pid_t pid, char *app_name, char *pkg_name, int apptype)
+{
+ int ret = 0, oom_score_adj = 0, notitype;
+ char pidbuf[MAX_DEC_SIZE(int)];
+ struct proc_status ps = {0};
+ struct proc_program_info *ppi;
+
+ if (status != PROC_CGROUP_SET_TERMINATED &&
+ status != PROC_CGROUP_SET_NOTI_REQUEST) {
+ if (!pid) {
+ _E("invalid pid : %d of %s", pid, app_name ? app_name : "noprocess");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = proc_get_oom_score_adj(pid, &oom_score_adj);
+ if (ret < 0) {
+ _E("Empty pid or process not exists. %d", pid);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ ps.pid = pid;
+ ps.appid = app_name;
+ ps.pai = NULL;
+ switch (status) {
+ case PROC_CGROUP_SET_FOREGRD:
+ _SD("set foreground : %d", pid);
+ ps.pai = find_app_info(pid);
+ if (apptype == PROC_TYPE_WIDGET || apptype == PROC_TYPE_WATCH) {
+ if (!ps.pai)
+ proc_add_app_list(apptype, pid, app_name, pkg_name);
+ proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED);
+ resourced_notify(RESOURCED_NOTIFIER_WIDGET_FOREGRD, &ps);
+ break;
+ } else {
+ snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
+ dbus_proc_handler(PREDEF_FOREGRD, pidbuf);
+ ret = proc_set_foregrd(pid, oom_score_adj);
+ if (ret != 0)
+ return RESOURCED_ERROR_NO_DATA;
+ notitype = RESOURCED_NOTIFIER_APP_FOREGRD;
+ }
+ if (ps.pai) {
+ ps.appid = ps.pai->appid;
+ resourced_notify(notitype, &ps);
+ }
+
+ if (proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ break;
+
+ if (apptype == PROC_TYPE_GUI)
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ break;
+ case PROC_CGROUP_SET_LAUNCH_REQUEST:
+ proc_set_oom_score_adj(pid, OOMADJ_INIT);
+ if (!app_name) {
+ _E("need application name!pid = %d", pid);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ _SD("launch request %s, %d", app_name, pid);
+ if (pkg_name)
+ _SD("launch request %s with pkgname", pkg_name);
+ ps.pai = proc_add_app_list(apptype, pid, app_name, pkg_name);
+ ret = resourced_proc_excluded(app_name);
+ if (!ps.pai || ret)
+ break;
+
+ if (CHECK_BIT(ps.pai->flags, PROC_VIP_ATTRIBUTE))
+ proc_set_oom_score_adj(pid, OOMADJ_SU);
+
+ if (ps.pai->categories)
+ proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+ resourced_notify(RESOURCED_NOTIFIER_APP_LAUNCH, &ps);
+ _D("available memory = %u", proc_get_mem_available());
+ if (proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ break;
+ ppi = ps.pai->program;
+ if (ppi->svc_list)
+ resourced_notify(RESOURCED_NOTIFIER_SERVICE_WAKEUP, &ps);
+ break;
+ case PROC_CGROUP_SET_SERVICE_REQUEST:
+ if (!app_name) {
+ _E("need application name!pid = %d", pid);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ _SD("service launch request %s, %d", app_name, pid);
+ if (pkg_name)
+ _SD("launch request %s with pkgname", pkg_name);
+ ps.pai = proc_add_app_list(PROC_TYPE_SERVICE,
+ pid, app_name, pkg_name);
+ if (!ps.pai)
+ break;
+ if (resourced_proc_excluded(app_name) == RESOURCED_ERROR_NONE)
+ resourced_notify(RESOURCED_NOTIFIER_SERVICE_LAUNCH, &ps);
+ if (!(CHECK_BIT(ps.pai->flags, PROC_BGCTRL_APP)) ||
+ ps.pai->categories)
+ proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+ break;
+ case PROC_CGROUP_SET_RESUME_REQUEST:
+ _SD("resume request %d", pid);
+ /* init oom_score_value */
+ if (!app_name) {
+ _E("need application name!pid = %d", pid);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ ps.pai = find_app_info(pid);
+ if (!ps.pai && ! resourced_proc_excluded(app_name))
+ ps.pai = proc_add_app_list(PROC_TYPE_GUI,
+ pid, app_name, pkg_name);
+
+ if (!ps.pai)
+ return RESOURCED_ERROR_NO_DATA;
+
+ ps.pai->lru_state = PROC_ACTIVE;
+ if (apptype == PROC_TYPE_GUI && oom_score_adj >= OOMADJ_FAVORITE) {
+ resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &ps);
+ proc_set_oom_score_adj(pid, OOMADJ_INIT);
+ }
+ _D("available memory = %u", proc_get_mem_available());
+ if (proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ break;
+ if (apptype == PROC_TYPE_GUI)
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ else if (apptype == PROC_TYPE_SERVICE)
+ resourced_notify(RESOURCED_NOTIFIER_SERVICE_WAKEUP, &ps);
+ break;
+ case PROC_CGROUP_SET_TERMINATE_REQUEST:
+ ps.pai = find_app_info(pid);
+ ps.pid = pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATE_START, &ps);
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ break;
+ case PROC_CGROUP_SET_ACTIVE:
+ ret = proc_set_active(pid, oom_score_adj);
+ if (ret != RESOURCED_ERROR_OK)
+ break;
+ resourced_notify(RESOURCED_NOTIFIER_APP_ACTIVE, &ps);
+ break;
+ case PROC_CGROUP_SET_BACKGRD:
+ if (apptype == PROC_TYPE_WIDGET || apptype == PROC_TYPE_WATCH) {
+ ps.pai = find_app_info(pid);
+ if (!ps.pai)
+ proc_add_app_list(apptype, pid, app_name, pkg_name);
+ proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_PERCEPTIBLE);
+ if (apptype == PROC_TYPE_WATCH)
+ break;
+ resourced_notify(RESOURCED_NOTIFIER_WIDGET_BACKGRD, &ps);
+ } else {
+ snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
+ dbus_proc_handler(PREDEF_BACKGRD, pidbuf);
+ ret = proc_set_backgrd(pid, oom_score_adj);
+ if (ret != 0)
+ break;
+ if ((proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ || resourced_freezer_proc_late_control())
+ break;
+
+ ps.pai = find_app_info(pid);
+ ps.pid = pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ }
+ break;
+ case PROC_CGROUP_SET_INACTIVE:
+ ret = proc_set_inactive(pid, oom_score_adj);
+ if (ret != RESOURCED_ERROR_OK)
+ break;
+ resourced_notify(RESOURCED_NOTIFIER_APP_INACTIVE, &ps);
+ break;
+ case PROC_CGROUP_GET_MEMSWEEP:
+ ret = proc_sweep_memory(PROC_SWEEP_EXCLUDE_ACTIVE, pid);
+ break;
+ case PROC_CGROUP_SET_NOTI_REQUEST:
+ if (!app_name || proc_get_freezer_status() == CGROUP_FREEZER_DISABLED)
+ break;
+ ps.pai = find_app_info_by_appid(app_name);
+ if (!ps.pai) {
+ _D("didn't have proc app list %s", app_name);
+ break;
+ }
+ ps.pid = ps.pai->main_pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ resourced_notify(RESOURCED_NOTIFIER_APP_ACTIVE, &ps);
+ break;
+ case PROC_CGROUP_SET_PROC_EXCLUDE_REQUEST:
+ proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+ break;
+ case PROC_CGROUP_SET_TERMINATED:
+ ps.pai = find_app_info(pid);
+ if (ps.pai)
+ ps.appid = ps.pai->appid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATED, &ps);
+ proc_remove_app_list(pid);
+ break;
+ case PROC_CGROUP_SET_SYSTEM_SERVICE:
+ if (oom_score_adj < OOMADJ_BACKGRD_PERCEPTIBLE)
+ proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_PERCEPTIBLE);
+ resourced_notify(RESOURCED_NOTIFIER_SYSTEM_SERVICE, &ps);
+ break;
+ default:
+ ret = RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ return ret;
+}
+
+int resourced_proc_action(int status, int argnum, char **arg)
+{
+ pid_t pid;
+ char *pidbuf = NULL, *cgroup_name = NULL, *pkg_name = NULL;
+ if (argnum < 1) {
+ _E("Unsupported number of arguments!");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ pidbuf = arg[0];
+ pid = (pid_t)atoi(pidbuf);
+ if (pid < 0) {
+ _E("Invalid pid argument!");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ /* Getting appid */
+ if (argnum > 1)
+ /* It's possible to get appid from arg */
+ cgroup_name = arg[1];
+ if (argnum == 3)
+ pkg_name = arg[2];
+ _SD("appid %s, pid %d, status %d\n", cgroup_name, pid, status);
+ return resourced_proc_status_change(status, pid, cgroup_name, pkg_name, PROC_TYPE_GUI);
+}
+
+int proc_get_state(int type, pid_t pid, char *buf, int len)
+{
+ int ret = 0;
+ switch (type) {
+ case PROC_CGROUP_GET_CMDLINE:
+ ret = proc_get_raw_cmdline(pid, buf, len);
+ break;
+ case PROC_CGROUP_GET_PGID_CMDLINE:
+ ret = proc_get_raw_cmdline(getpgid(pid), buf, len);
+ break;
+ case PROC_CGROUP_GET_EXE:
+ ret = proc_get_exepath(pid, buf, len);
+ break;
+ case PROC_CGROUP_GET_STAT:
+ ret = proc_get_stat(pid, buf, len);
+ break;
+ case PROC_CGROUP_GET_STATUS:
+ ret = proc_get_status(pid, buf, len);
+ break;
+ case PROC_CGROUP_GET_OOMSCORE:
+ ret = proc_get_oom_score_adj(pid, (int *)buf);
+ break;
+ default:
+ _E("unsupported command %d, pid(%d)", type, pid);
+ ret = RESOURCED_ERROR_FAIL;
+ break;
+ }
+ return ret;
+}
+
+void resourced_proc_dump(int mode, const char *dirpath)
+{
+ char buf[MAX_PATH_LENGTH];
+ _cleanup_fclose_ FILE *f = NULL;
+
+ if (dirpath) {
+ time_t now;
+ struct tm cur_tm;
+
+ now = time(NULL);
+ if (localtime_r(&now, &cur_tm) == NULL)
+ _E("Fail to get localtime");
+
+ snprintf(buf, sizeof(buf), "%s/%s_%.4d%.2d%.2d%.2d%.2d%.2d.log",
+ dirpath, LOG_PREFIX, (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);
+ f = fopen(buf, "w+");
+ }
+ proc_dump_process_list(f);
+ modules_dump((void *)f, mode);
+}
+
+static const struct module_ops proc_modules_ops = {
+ .priority = MODULE_PRIORITY_EARLY,
+ .name = "PROC",
+ .init = resourced_proc_init,
+ .exit = resourced_proc_exit,
+};
+
+MODULE_REGISTER(&proc_modules_ops)
--- /dev/null
+/*
+
+ * 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 proc-monitor.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <Ecore.h>
+#include <E_DBus.h>
+#include <sys/mount.h>
+
+#include "proc-main.h"
+#include "proc-monitor.h"
+#include "resourced.h"
+#include "macro.h"
+#include "trace.h"
+#include "edbus-handler.h"
+#include "proc-process.h"
+#include "procfs.h"
+#include "lowmem-handler.h"
+#include "notifier.h"
+#include "init.h"
+#include "module.h"
+
+#define WATCHDOG_LAUNCHING_PARAM "WatchdogPopupLaunch"
+#define WATCHDOG_KEY1 "_SYSPOPUP_CONTENT_"
+#define WATCHDOG_KEY2 "_APP_NAME_"
+#define WATCHDOG_VALUE_1 "watchdog"
+
+#define TIZEN_DEBUG_MODE_FILE "/opt/etc/.debugmode"
+
+#define INIT_PID 1
+#define INIT_PROC_VAL -1
+
+static int proc_watchdog_state;
+int current_lcd_state;
+
+static Ecore_Timer *watchdog_check_timer;
+#define WATCHDOG_TIMER_INTERVAL 10
+
+static struct proc_watchdog_info {
+ pid_t pid;
+ int signum;
+} proc_watchdog = { -1, -1 };
+
+int proc_debug_enabled(void)
+{
+ if (access(TIZEN_DEBUG_MODE_FILE, F_OK) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+void proc_set_watchdog_state(int state)
+{
+ proc_watchdog_state = state;
+}
+
+static int proc_get_watchdog_state(void)
+{
+ return proc_watchdog_state;
+}
+
+static DBusMessage *edbus_get_meminfo(E_DBus_Object *obj, DBusMessage *msg)
+{
+ unsigned int mem_total, mem_free, mem_available, cached, used;
+ unsigned int swap_total, swap_free, swap;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ struct meminfo mi;
+ int r;
+
+ reply = dbus_message_new_method_return(msg);
+
+ r = proc_get_meminfo(&mi,
+ MEMINFO_MASK_MEM_TOTAL |
+ MEMINFO_MASK_MEM_FREE |
+ MEMINFO_MASK_MEM_AVAILABLE |
+ MEMINFO_MASK_CACHED |
+ MEMINFO_MASK_SWAP_TOTAL |
+ MEMINFO_MASK_SWAP_FREE);
+ if (r < 0) {
+ _E("Failed to get meminfo: %s", strerror(-r));
+ return reply;
+ }
+
+ mem_total = mi.value[MEMINFO_ID_MEM_TOTAL];
+ mem_free = mi.value[MEMINFO_ID_MEM_FREE];
+ mem_available = mi.value[MEMINFO_ID_MEM_AVAILABLE];
+ cached = mi.value[MEMINFO_ID_CACHED];
+ swap_total = mi.value[MEMINFO_ID_SWAP_TOTAL];
+ swap_free = mi.value[MEMINFO_ID_SWAP_FREE];
+
+ used = mem_total - mem_available;
+ swap = swap_total - swap_free;
+
+ _D("get memory info total = %u, free = %u, cache = %u, used = %u, swap = %u",
+ mem_total, mem_free, cached, used, swap);
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &mem_total);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &mem_free);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &cached);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &used);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &swap);
+
+ return reply;
+}
+
+static DBusMessage *edbus_reclaim_memory(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusError err;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ int ret = -1;
+
+ dbus_error_init(&err);
+ _D("edbus_reclaim_memory");
+
+ ret = proc_sys_node_trigger(SYS_VM_SHRINK_MEMORY);
+ ret = proc_sys_node_trigger(SYS_VM_COMPACT_MEMORY);
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+static DBusMessage *edbus_pre_poweroff(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ int ret = -1;
+
+ _D("edbus_poweroff");
+
+ proc_sweep_memory(PROC_SWEEP_EXCLUDE_ACTIVE, INIT_PID);
+ resourced_notify(RESOURCED_NOTIFIER_POWER_OFF, NULL);
+ umount2("/sys/fs/cgroup", MNT_FORCE|MNT_DETACH);
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+}
+
+static void proc_dbus_active_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret, type;
+ char *str;
+ pid_t pid;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_ACTIVE);
+ if (ret == 0) {
+ _D("there is no active signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &str, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ if (!strcmp(str, "active"))
+ type = PROC_CGROUP_SET_ACTIVE;
+ else if (!strcmp(str, "inactive"))
+ type = PROC_CGROUP_SET_INACTIVE;
+ else
+ return;
+ resourced_proc_status_change(type, pid, NULL, NULL, PROC_TYPE_NONE);
+}
+
+static DBusMessage *edbus_get_app_cpu(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ char *appid;
+ unsigned long total, utime, stime;
+ struct proc_app_info *pai = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ pai = find_app_info_by_appid(appid);
+ if (!pai) {
+ _E("There is no appid %s", appid);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (proc_get_cpu_time(pai->main_pid, &utime, &stime) != RESOURCED_ERROR_NONE) {
+ _E("proc_get_cpu_time = %s (%d)", appid, pai->main_pid);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+ _D("get cpu usage for %s (%d), utime = %u, stime = %u",
+ appid, pai->main_pid, utime, stime);
+ total = utime + stime;
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &total);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_app_memory(E_DBus_Object *obj, DBusMessage *msg)
+{
+ int ret;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ char *appid;
+ unsigned int rss;
+ struct proc_app_info *pai = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &appid,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ pai = find_app_info_by_appid(appid);
+ if (!pai || !pai->main_pid) {
+ _E("There is no appid %s", appid);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0) {
+ _E("lowmem_get_proc_mem_usage failed for appid = %s (%d)",
+ appid, pai->main_pid);
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ _D("get memory usage for %s (%d), rss = %u", appid, pai->main_pid, rss);
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &rss);
+
+ return reply;
+}
+
+static DBusMessage *edbus_get_memory_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+ GSList *giter;
+ char *appid;
+ struct proc_app_info *pai;
+ unsigned int total = 0, rss;
+
+ reply = dbus_message_new_method_return(msg);
+ gslist_for_each_item(giter, proc_app_list) {
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->main_pid)
+ continue;
+ if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ continue;
+ total += rss;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(su)", &arr);
+ gslist_for_each_item(giter, proc_app_list) {
+ DBusMessageIter sub;
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai || !pai->main_pid)
+ continue;
+ if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ continue;
+
+ appid = pai->appid;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &rss);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&iter, &arr);
+ return reply;
+}
+
+static DBusMessage *edbus_get_cpu_list(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+ GSList *giter;
+ char *appid;
+ struct proc_app_info *pai;
+ unsigned long total, utime, stime;
+
+ total = 0;
+ reply = dbus_message_new_method_return(msg);
+ gslist_for_each_item(giter, proc_app_list) {
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->main_pid)
+ continue;
+ if (proc_get_cpu_time(pai->main_pid, &utime, &stime) != RESOURCED_ERROR_NONE) {
+ continue;
+ }
+ total += utime;
+ total += stime;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(su)", &arr);
+ gslist_for_each_item(giter, proc_app_list) {
+ DBusMessageIter sub;
+ unsigned long percent;
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->main_pid)
+ continue;
+ if (proc_get_cpu_time(pai->main_pid, &utime, &stime) != RESOURCED_ERROR_NONE) {
+ continue;
+ }
+ appid = pai->appid;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ percent = (!total) ? 0 : ((((utime + stime) * 1000)/total + 5) / 10);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &percent);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&iter, &arr);
+ return reply;
+}
+
+static DBusMessage *edbus_get_memory_lists(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+ GSList *giter;
+ char *appid;
+ int type, ret;
+ struct proc_app_info *pai;
+ unsigned int rss;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &type,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(su)", &arr);
+ gslist_for_each_item(giter, proc_app_list) {
+ DBusMessageIter sub;
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai || !pai->main_pid)
+ continue;
+ if (type != PROC_TYPE_MAX && pai->type != type)
+ continue;
+ if (proc_get_mem_usage(pai->main_pid, NULL, &rss) < 0)
+ continue;
+
+ appid = pai->appid;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &rss);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&iter, &arr);
+ return reply;
+}
+
+static DBusMessage *edbus_get_cpu_lists(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessageIter arr;
+ DBusMessage *reply;
+ GSList *giter;
+ int ret, type;
+ char *appid;
+ struct proc_app_info *pai;
+ unsigned long total, utime, stime;
+
+ total = 0;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &type,
+ DBUS_TYPE_INVALID);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ gslist_for_each_item(giter, proc_app_list) {
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->main_pid)
+ continue;
+ if (type != PROC_TYPE_MAX && pai->type != type)
+ continue;
+ if (proc_get_cpu_time(pai->main_pid, &utime, &stime) != RESOURCED_ERROR_NONE) {
+ continue;
+ }
+ total += utime;
+ total += stime;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(su)", &arr);
+ gslist_for_each_item(giter, proc_app_list) {
+ DBusMessageIter sub;
+ unsigned long percent;
+ pai = (struct proc_app_info *)giter->data;
+ if (!pai->main_pid)
+ continue;
+ if (type != PROC_TYPE_MAX && pai->type != type)
+ continue;
+ if (proc_get_cpu_time(pai->main_pid, &utime, &stime) != RESOURCED_ERROR_NONE) {
+ continue;
+ }
+ appid = pai->appid;
+ dbus_message_iter_open_container(&arr, DBUS_TYPE_STRUCT, NULL, &sub);
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appid);
+ if (total == 0)
+ percent = 0;
+ else
+ percent = (((utime + stime) * 1000)/total + 5) / 10;
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &percent);
+ dbus_message_iter_close_container(&arr, &sub);
+ }
+ dbus_message_iter_close_container(&iter, &arr);
+ return reply;
+}
+
+static void proc_dbus_exclude_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ char *str;
+ pid_t pid;
+ struct proc_exclude pe;
+ int len;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_EXCLUDE);
+ if (ret == 0) {
+ _D("there is no active signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &str, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ _D("call proc_dbus_exclude_signal_handler : pid = %d, str = %s", pid, str);
+
+ /*
+ * allowed strings: wakeup, exclude, include
+ */
+ len = strlen(str);
+
+ if (len == 6 && !strncmp(str, "wa", 2)) {
+ struct proc_status ps = {0};
+
+ ps.pai = find_app_info(pid);
+ ps.pid = pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ } else if (len == 7) {
+ if(!strncmp(str, "ex", 2)) {
+ pe.pid = pid;
+ pe.type = PROC_EXCLUDE;
+ resourced_notify(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, &pe);
+ proc_set_runtime_exclude_list(pe.pid, pe.type);
+ } else if (!strncmp(str, "in", 2)) {
+ pe.pid = pid;
+ pe.type = PROC_INCLUDE;
+ resourced_notify(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, &pe);
+ proc_set_runtime_exclude_list(pe.pid, pe.type);
+ }
+ } else
+ return;
+}
+
+static void proc_dbus_exclude_appid_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ char *str;
+ char *appid;
+ struct proc_exclude pe;
+ int len;
+ struct proc_status ps = {0};
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_EXCLUDEAPPID);
+ if (ret == 0) {
+ _D("there is no active signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_STRING, &appid, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ if (!appid)
+ return;
+
+ _D("proc exclude : appid = %s, str = %s", appid, str);
+
+ ps.pai = find_app_info_by_appid(appid);
+ if (!ps.pai) {
+ _E("don't find any process info about %s", appid);
+ return;
+ }
+ ps.pid = ps.pai->main_pid;
+
+ /*
+ * allowed strings: wakeup, exclude, include
+ */
+ len = strlen(str);
+
+ if (len == 6 && !strncmp(str, "wa", 2)) {
+ resourced_notify(RESOURCED_NOTIFIER_APP_WAKEUP, &ps);
+ } else if (len == 7) {
+ if(!strncmp(str, "ex", 2)) {
+ pe.pid = ps.pid;
+ pe.type = PROC_EXCLUDE;
+ resourced_notify(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, &pe);
+ proc_set_runtime_exclude_list(pe.pid, pe.type);
+ } else if (!strncmp(str, "in", 2)) {
+ pe.pid = ps.pid;
+ pe.type = PROC_INCLUDE;
+ resourced_notify(RESOURCED_NOTIFIER_CONTROL_EXCLUDE, &pe);
+ proc_set_runtime_exclude_list(pe.pid, pe.type);
+ }
+ } else
+ return;
+}
+
+
+static void proc_dbus_prelaunch_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ char *appid;
+ char *pkgid;
+ int flags, categories;
+ struct proc_status ps;
+ struct proc_app_info *pai;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_PRELAUNCH);
+ if (ret == 0) {
+ _D("there is no prelaunch signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err,
+ DBUS_TYPE_STRING, &appid,
+ DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_INT32, &flags,
+ DBUS_TYPE_INT32, &categories, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ _D("call proc_dbus_prelaunch_handler: appid = %s, pkgid = %s, flags = %d,"
+ " categories = %X\n", appid, pkgid, flags, categories);
+
+ pai = proc_create_app_list(appid, pkgid);
+ if (!pai) {
+ _E("Failed to create app list");
+ return;
+ }
+
+ pai->flags = flags;
+ pai->type = PROC_TYPE_READY;
+ pai->categories = categories;
+ ps.appid = appid;
+ ps.pai = pai;
+ resourced_notify(RESOURCED_NOTIFIER_APP_PRELAUNCH, &ps);
+ lowmem_proactive_oom_killer(flags, appid);
+}
+
+static void proc_dbus_sweep_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_SWEEP);
+
+ if (ret == 0) {
+ _D("there is no sweep signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+ proc_sweep_memory(PROC_SWEEP_INCLUDE_ACTIVE, INIT_PID);
+}
+
+static Eina_Bool check_watchdog_cb(void *data)
+{
+ int oom_score_adj = 0, ret;
+ pid_t pid = proc_watchdog.pid;
+
+ ret = proc_get_oom_score_adj(pid, &oom_score_adj);
+ if (!ret) {
+ _E("watchdog pid: %d not terminated pid: %d kill again\n", pid);
+ kill(pid, SIGKILL);
+ }
+ ecore_timer_del(watchdog_check_timer);
+ watchdog_check_timer = NULL;
+ proc_watchdog.pid = -1;
+ proc_watchdog.signum = -1;
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static void proc_dbus_watchdog_result(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int type;
+
+ if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG_RESULT) == 0) {
+ _D("there is no watchdog result signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ if (type == 1) {
+ if (proc_watchdog.signum == SIGTERM || proc_watchdog.signum == SIGKILL) {
+ resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
+ proc_watchdog.pid, NULL, NULL, PROC_TYPE_NONE);
+ kill(proc_watchdog.pid, SIGABRT);
+ if (watchdog_check_timer == NULL) {
+ watchdog_check_timer =
+ ecore_timer_add(WATCHDOG_TIMER_INTERVAL, check_watchdog_cb, (void *)NULL);
+ }
+ } else {
+ _E("ERROR: Unsupported signal type!");
+ }
+ }
+ proc_watchdog.pid = -1;
+ proc_watchdog.signum = -1;
+}
+
+static int proc_dbus_show_popup(const char *value)
+{
+ DBusError err;
+ DBusMessage *msg;
+ char str_val[32];
+ char *pa[4];
+ int i, ret, ret_val;
+
+ snprintf(str_val, sizeof(str_val), "%s", value);
+
+ pa[0] = WATCHDOG_KEY1;
+ pa[1] = WATCHDOG_VALUE_1;
+ pa[2] = WATCHDOG_KEY2;
+ pa[3] = str_val;
+ i = 0;
+
+ do {
+ msg = dbus_method_sync(SYSTEM_POPUP_BUS_NAME, SYSTEM_POPUP_PATH_SYSTEM,
+ SYSTEM_POPUP_IFACE_SYSTEM, WATCHDOG_LAUNCHING_PARAM, "ssss", 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 void proc_dbus_watchdog_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int pid, command, ret;
+ char appname[PROC_NAME_MAX];
+ struct proc_status ps;
+
+ if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_WATCHDOG) == 0) {
+ _D("there is no watchdog result signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INT32,
+ &command, DBUS_TYPE_INVALID);
+
+ if (ret == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ ret = proc_get_cmdline(pid, appname);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("ERROR : invalid pid(%d)", pid);
+ return;
+ }
+
+ ret = resourced_proc_excluded(appname);
+ if (ret == RESOURCED_ERROR_NONMONITOR)
+ return;
+
+ if (current_lcd_state == LCD_STATE_OFF) {
+ _E("Receive watchdog signal to pid: %d(%s) but don't show ANR popup in LCD off state\n", pid, appname);
+ return;
+ }
+
+ _E("Receive watchdog signal to pid: %d(%s)\n", pid, appname);
+ ps.pai = find_app_info(pid);
+ ps.pid = pid;
+ resourced_notify(RESOURCED_NOTIFIER_APP_ANR, &ps);
+
+ if (watchdog_check_timer) {
+ if (proc_watchdog.pid == pid) {
+ _E("pid %d(%s) has already received watchdog siganl but not terminated", pid, appname);
+ kill(pid, SIGKILL);
+ proc_watchdog.pid = -1;
+ proc_watchdog.signum = -1;
+ return;
+ }
+ }
+
+ _E("just kill watchdog process when debug enabled pid: %d(%s)\n", pid, appname);
+ resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
+ pid, NULL, NULL, PROC_TYPE_NONE);
+ kill(pid, SIGABRT);
+ if (watchdog_check_timer == NULL) {
+ watchdog_check_timer =
+ ecore_timer_add(WATCHDOG_TIMER_INTERVAL,
+ check_watchdog_cb, (void *)NULL);
+ proc_watchdog.pid = pid;
+ proc_watchdog.signum = command;
+ }
+}
+
+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 void proc_dbus_systemservice_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_bool_t ret;
+ pid_t pid;
+
+ if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_SYSTEMSERVICE) == 0) {
+ _D("there is no process group signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID);
+ if (ret == 0) {
+ _D("there is no message");
+ return;
+ }
+ resourced_proc_status_change(PROC_CGROUP_SET_SYSTEM_SERVICE, pid,
+ NULL, NULL, PROC_TYPE_NONE);
+}
+
+static DBusMessage *edbus_signal_trigger(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ dbus_bool_t ret;
+ int pid, command, ret_val;
+ char appname[PROC_NAME_MAX];
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INT32,
+ &command, DBUS_TYPE_INVALID);
+
+ if (ret == TRUE) {
+ ret_val = proc_get_cmdline(pid, appname);
+ if (ret_val != RESOURCED_ERROR_NONE) {
+ _E("ERROR : invalid pid(%d)", pid);
+ } else {
+ _E("Receive watchdog signal to pid: %d(%s)\n", pid, appname);
+ if (proc_get_watchdog_state() == PROC_WATCHDOG_ENABLE && proc_watchdog.pid == -1) {
+ ret_val = resourced_proc_excluded(appname);
+ if (ret_val == RESOURCED_ERROR_NONE) {
+ ret_val = proc_dbus_show_popup(appname);
+ if (ret_val < 0)
+ _E("ERROR : request_to_launch_by_dbus()failed : %d", ret_val);
+ else {
+ proc_watchdog.pid = pid;
+ proc_watchdog.signum = command;
+ }
+ }
+ }
+ }
+ } else {
+ _E("ERROR: Wrong message arguments!");
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+}
+
+static void proc_dbus_lcd_on(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDON) == 0) {
+ _D("there is no lcd on signal");
+ return;
+ }
+ dbus_error_free(&err);
+ current_lcd_state = LCD_STATE_ON;
+ resourced_notify(RESOURCED_NOTIFIER_LCD_ON, NULL);
+ /* nothing */
+}
+
+static void proc_dbus_lcd_off(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDOFF) == 0) {
+ _D("there is no lcd on signal");
+ return;
+ }
+
+ dbus_error_free(&err);
+ current_lcd_state = LCD_STATE_OFF;
+ resourced_notify(RESOURCED_NOTIFIER_LCD_OFF, NULL);
+}
+
+static void booting_done_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_CORE,
+ SIGNAL_DEVICED_BOOTINGDONE) == 0) {
+ _D("there is no lcd on signal");
+ return;
+ }
+ /*
+ * initialize all modules again
+ * If some modules weren't initialized at this time,
+ * it could get a change again for initializing.
+ * Because modules_init checked whether it was already intialized,
+ * it didn't initialize modules twice.
+ */
+ modules_init(NULL);
+ resourced_notify(RESOURCED_NOTIFIER_BOOTING_DONE, NULL);
+}
+
+static void early_booting_done_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_CORE,
+ SIGNAL_DEVICED_EARLY_BOOTING_DONE) == 0) {
+ _D("there is no lcd on signal");
+ return;
+ }
+ modules_late_init(NULL);
+}
+
+static void low_battery_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_BATTERY,
+ SIGNAL_DEVICED_LOW_BATTERY) == 0) {
+ _D("there is no low battery signal");
+ return;
+ }
+
+ resourced_notify(RESOURCED_NOTIFIER_LOW_BATTERY, NULL);
+}
+
+static void poweroff_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_POWEROFF,
+ SIGNAL_DEVICED_POWEROFF_STATE) == 0) {
+ _D("there is no power off signal");
+ return;
+ }
+
+ _E("quit mainloop at poweroff");
+ resourced_quit_mainloop();
+}
+
+static void systemtime_changed_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+
+ dbus_error_init(&err);
+ if (dbus_message_is_signal(msg, DEVICED_INTERFACE_TIME,
+ SIGNAL_DEVICED_SYSTEMTIME_CHANGED) == 0) {
+ _D("there is no system time changed signal");
+ return;
+ }
+
+ resourced_notify(RESOURCED_NOTIFIER_SYSTEMTIME_CHANGED, NULL);
+}
+
+static void proc_dbus_aul_launch(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ int status, apptype;
+ char *appid, *pkgid, *pkgtype;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_LAUNCH) == 0) {
+ _D("there is no aul launch signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_STRING, &appid, DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_STRING, &pkgtype, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ if (!strncmp(pkgtype, "svc", 3)) {
+ apptype = PROC_TYPE_SERVICE;
+ status = PROC_CGROUP_SET_SERVICE_REQUEST;
+ } else if (!strncmp(pkgtype, "ui", 2)) {
+ apptype = PROC_TYPE_GUI;
+ status = PROC_CGROUP_SET_LAUNCH_REQUEST;
+ } else if (!strncmp(pkgtype, "widget", 6)) {
+ apptype = PROC_TYPE_WIDGET;
+ status = PROC_CGROUP_SET_LAUNCH_REQUEST;
+ } else if (!strncmp(pkgtype, "watch", 5)) {
+ apptype = PROC_TYPE_WATCH;
+ status = PROC_CGROUP_SET_LAUNCH_REQUEST;
+ } else
+ return;
+
+ resourced_proc_status_change(status, pid, appid, pkgid, apptype);
+}
+
+static void proc_dbus_aul_resume(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ int status = PROC_CGROUP_SET_RESUME_REQUEST, apptype;
+ char *appid, *pkgid, *pkgtype;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_RESUME) == 0) {
+ _D("there is no aul resume signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_STRING, &appid, DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_STRING, &pkgtype, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ if (!strncmp(pkgtype, "svc", 3))
+ apptype = PROC_TYPE_SERVICE;
+ else if (!strncmp(pkgtype, "widget", 6))
+ apptype = PROC_TYPE_WIDGET;
+ else if (!strncmp(pkgtype, "watch", 5))
+ apptype = PROC_TYPE_WATCH;
+ else
+ apptype = PROC_TYPE_GUI;
+
+ resourced_proc_status_change(status, pid, appid, pkgid, apptype);
+}
+
+static void proc_dbus_aul_terminate(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ int status = PROC_CGROUP_SET_TERMINATE_REQUEST;
+ char *appid, *pkgid, *pkgtype;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_TERMINATE) == 0) {
+ _D("there is no aul terminate signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_STRING, &appid, DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_STRING, &pkgtype, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ resourced_proc_status_change(status, pid, appid, pkgid, PROC_TYPE_NONE);
+}
+
+static void proc_dbus_aul_changestate(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ int status, apptype;
+ char *appid, *pkgid, *statstr, *pkgtype;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_STATE) == 0) {
+ _D("there is no aul changestate signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_STRING, &appid, DBUS_TYPE_STRING, &pkgid,
+ DBUS_TYPE_STRING, &statstr, DBUS_TYPE_STRING, &pkgtype,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ if (!strncmp(statstr, "fg", 2))
+ status = PROC_CGROUP_SET_FOREGRD;
+ else if (!strncmp(statstr, "bg", 2))
+ status = PROC_CGROUP_SET_BACKGRD;
+ else
+ return;
+
+ if (!strncmp(pkgtype, "svc", 3))
+ apptype = PROC_TYPE_SERVICE;
+ else if (!strncmp(pkgtype, "widget", 6))
+ apptype = PROC_TYPE_WIDGET;
+ else if (!strncmp(pkgtype, "watch", 5))
+ apptype = PROC_TYPE_WATCH;
+ else
+ apptype = PROC_TYPE_GUI;
+
+ resourced_proc_status_change(status, pid, appid, pkgid, apptype);
+}
+
+static void proc_dbus_aul_group(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t ownerpid, childpid;
+ char *appid;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_GROUP) == 0) {
+ _D("there is no aul group signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &ownerpid,
+ DBUS_TYPE_INT32, &childpid, DBUS_TYPE_STRING, &appid,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ _D("received process grouping : owner %d, child %d, previous appid %s",
+ ownerpid, childpid, appid);
+ proc_set_group(ownerpid, childpid, appid);
+}
+
+static void proc_dbus_aul_terminated(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ int status = PROC_CGROUP_SET_TERMINATED;
+
+ if (dbus_message_is_signal(msg, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_TERMINATED) == 0) {
+ _D("there is no aul terminate signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ _D("received terminated process : pid %d", pid);
+ resourced_proc_status_change(status, pid, NULL, NULL, PROC_TYPE_NONE);
+}
+
+static void proc_dbus_suspend_hint(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ dbus_error_init(&err);
+ pid_t pid;
+ struct proc_app_info *pai = NULL;
+ struct proc_status ps = {0};
+ enum proc_state state;
+
+ if (dbus_message_is_signal(msg, AUL_SUSPEND_INTERFACE_NAME,
+ SIGNAL_AMD_SUSPNED) == 0) {
+ _D("there is no aul terminate signal");
+ return;
+ }
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid,
+ DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+ dbus_error_free(&err);
+
+ _D("received susnepd hint : pid %d", pid);
+ pai = find_app_info(pid);
+ if (!pai)
+ return;
+
+ state = proc_check_suspend_state(pai);
+ if (state == PROC_STATE_SUSPEND) {
+ ps.pid = pid;
+ ps.pai = pai;
+ resourced_notify(RESOURCED_NOTIFIER_APP_SUSPEND,
+ &ps);
+ }
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { "Signal", "ii", NULL, edbus_signal_trigger },
+ { "GetAppCpu", "s", "u", edbus_get_app_cpu },
+ { "GetCpuList", NULL, "a(su)", edbus_get_cpu_list },
+ { "GetCpuLists", "i", "a(su)", edbus_get_cpu_lists },
+ { "GetAppMemory", "s", "u", edbus_get_app_memory },
+ { "GetMemoryList", NULL, "a(su)", edbus_get_memory_list },
+ { "GetMemoryLists", "i", "a(su)", edbus_get_memory_lists },
+ { "GetMemInfo", NULL, "uuuuu", edbus_get_meminfo },
+ { "ReclaimMemory", NULL, NULL, edbus_reclaim_memory },
+ { "PrePoweroff", NULL, NULL, edbus_pre_poweroff },
+ /* Add methods here */
+};
+
+static const struct edbus_signal edbus_signals[] = {
+ /* RESOURCED DBUS */
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_WATCHDOG_RESULT, proc_dbus_watchdog_result, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_ACTIVE, proc_dbus_active_signal_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_EXCLUDE, proc_dbus_exclude_signal_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_PRELAUNCH, proc_dbus_prelaunch_signal_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_SWEEP, proc_dbus_sweep_signal_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_WATCHDOG, proc_dbus_watchdog_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_SYSTEMSERVICE, proc_dbus_systemservice_handler, NULL},
+ {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+ SIGNAL_PROC_EXCLUDEAPPID, proc_dbus_exclude_appid_signal_handler, NULL},
+
+ /* DEVICED DBUS */
+ {DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDON, proc_dbus_lcd_on, NULL},
+ {DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDOFF, proc_dbus_lcd_off, NULL},
+ {DEVICED_PATH_CORE, DEVICED_INTERFACE_CORE,
+ SIGNAL_DEVICED_BOOTINGDONE, booting_done_signal_handler, NULL},
+ {DEVICED_PATH_POWEROFF, DEVICED_INTERFACE_POWEROFF,
+ SIGNAL_DEVICED_POWEROFF_STATE, poweroff_signal_handler, NULL},
+ {DEVICED_PATH_BATTERY, DEVICED_INTERFACE_BATTERY,
+ SIGNAL_DEVICED_LOW_BATTERY, low_battery_signal_handler, NULL},
+ {DUMP_SERVICE_OBJECT_PATH, DUMP_SERVICE_INTERFACE_NAME,
+ SIGNAL_DUMP, proc_dbus_dump_handler, NULL},
+ {DEVICED_PATH_CORE, DEVICED_INTERFACE_CORE,
+ SIGNAL_DEVICED_EARLY_BOOTING_DONE,
+ early_booting_done_signal_handler, NULL},
+ {DEVICED_PATH_TIME, DEVICED_INTERFACE_TIME,
+ SIGNAL_DEVICED_SYSTEMTIME_CHANGED,
+ systemtime_changed_signal_handler, NULL},
+
+ /* AMD DBUS */
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_LAUNCH, proc_dbus_aul_launch, NULL},
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_RESUME, proc_dbus_aul_resume, NULL},
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_TERMINATE, proc_dbus_aul_terminate, NULL},
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_STATE, proc_dbus_aul_changestate, NULL},
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_GROUP, proc_dbus_aul_group, NULL},
+ {AUL_APPSTATUS_OBJECT_PATH, AUL_APPSTATUS_INTERFACE_NAME,
+ SIGNAL_AMD_TERMINATED, proc_dbus_aul_terminated, NULL},
+ {AUL_SUSPEND_OBJECT_PATH, AUL_SUSPEND_INTERFACE_NAME,
+ SIGNAL_AMD_SUSPNED, proc_dbus_suspend_hint, NULL},
+};
+
+static int proc_dbus_init(void *data)
+{
+ edbus_add_signals(edbus_signals, ARRAY_SIZE(edbus_signals));
+
+ /* start watchdog check timer for preveting ANR during booting */
+ watchdog_check_timer =
+ ecore_timer_add(WATCHDOG_TIMER_INTERVAL, check_watchdog_cb, (void *)NULL);
+
+ return edbus_add_methods(RESOURCED_PATH_PROCESS, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+}
+
+static int proc_dbus_exit(void *data)
+{
+ if (watchdog_check_timer)
+ ecore_timer_del(watchdog_check_timer);
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct proc_module_ops proc_dbus_ops = {
+ .name = "PROC_DBUS",
+ .init = proc_dbus_init,
+ .exit = proc_dbus_exit,
+};
+PROC_MODULE_REGISTER(&proc_dbus_ops)
--- /dev/null
+/*
+ * 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 proc_noti.c
+ * @desc It's main entry point for handling proc events
+ * @see proc_cgroup_cmd_type
+*/
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+
+#include <Ecore.h>
+#include <systemd/sd-daemon.h>
+
+#include "macro.h"
+#include "util.h"
+#include "proc-main.h"
+#include "proc-noti.h"
+#include "proc-process.h"
+#include "resourced.h"
+#include "trace.h"
+
+static int noti_fd;
+/*
+ * @desc function receives uint
+ * negative value for error reporting
+ */
+static inline int recv_int(int fd)
+{
+ int val = 0, r = -1;
+ while (1) {
+ r = read(fd, &val, sizeof(int));
+ if (r == sizeof(int))
+ return val;
+
+ if (errno == EINTR) {
+ _E("Re-read for error(EINTR)");
+ continue;
+ } else {
+ _E("Read fail for int");
+ return -errno;
+ }
+ }
+}
+
+static inline char *recv_str(int fd)
+{
+ int len, r = -1;
+ char *str;
+
+ while (1) {
+ r = read(fd, &len, sizeof(int));
+ if (r < 0) {
+ if (errno == EINTR) {
+ _E("Re-read for error(EINTR)");
+ continue;
+ } else {
+ _E("Read fail for str length");
+ return NULL;
+ }
+ } else
+ break;
+ }
+
+ if (len <= 0)
+ return NULL;
+
+ if (len >= INT_MAX) {
+ _E("size is over INT_MAX");
+ return NULL;
+ }
+
+ str = (char *)malloc(len + 1);
+ if (str == NULL) {
+ _E("Not enough memory");
+ return NULL;
+ }
+
+ while (1) {
+ r = read(fd, str, len);
+ if (r < 0) {
+ if (errno == EINTR) {
+ _E("Re-read for error(EINTR)");
+ continue;
+ } else {
+ _E("Read fail for str");
+ free(str);
+ return NULL;
+ }
+ } else
+ break;
+ }
+ str[len] = 0;
+
+ return str;
+}
+
+/*
+ * @desc This function read from fd to msg,
+ * it supports multiple argument list given from client
+ * of string type
+ * @return 0 on success errno constants in error case
+*/
+int read_message(int fd, struct resourced_noti *msg)
+{
+ int i;
+
+ ret_value_if(fd < 0, RESOURCED_ERROR_FAIL);
+ msg->pid = recv_int(fd);
+ ret_value_if(msg->pid <= 0, errno);
+ msg->type = recv_int(fd);
+ ret_value_if(msg->type <= 0, errno);
+ msg->argc = recv_int(fd);
+ ret_value_if(msg->argc <= 0, errno);
+
+ for (i = 0; i < msg->argc; ++i) {
+ msg->argv[i] = recv_str(fd);
+ ret_value_if(msg->argv[i] <= 0, errno);
+ }
+
+ return 0;
+}
+
+static bool _fatal_read_message_error(const int error_code)
+{
+ return error_code == EBADF || error_code == EISDIR;
+}
+
+static inline void internal_free(char *str)
+{
+ if (str)
+ free(str);
+}
+
+void free_message(struct resourced_noti *msg)
+{
+ int i;
+
+ for (i = 0; i < msg->argc; i++)
+ internal_free(msg->argv[i]);
+ free(msg);
+}
+
+static int process_message(struct resourced_noti *msg)
+{
+ _D("process message caller pid %d\n", msg->pid);
+ return resourced_proc_action(msg->type, msg->argc, msg->argv);
+}
+
+static void safe_write_int(int fd, int type, int *value)
+{
+ bool sync = SYNC_OPERATION(type);
+ int ret;
+ if (!sync) {
+ _D("Response is not needed");
+ return;
+ }
+
+ ret = write(fd, value, sizeof(int));
+ if (ret < 0)
+ ETRACE_ERRNO_MSG("Failed to response to client, %d", *value);
+}
+
+int write_response(int *retval, int fd, char *buf, int len)
+{
+ int ret;
+ ret = send(fd, retval, sizeof(int), 0);
+ if (ret < 0)
+ _E("Failed to send");
+ if (!buf)
+ return ret;
+ ret = send(fd, buf, len, 0);
+ if (ret < 0)
+ _E("Failed to write");
+ return ret;
+}
+
+static Eina_Bool proc_noti_cb(void *data, Ecore_Fd_Handler *fd_handler)
+{
+ int fd;
+ struct resourced_noti *msg;
+ int ret = -1;
+ struct sockaddr_un client_address;
+ int client_sockfd;
+ int client_len;
+ int error_code;
+ _cleanup_free_ char *send_buffer = NULL;
+ int send_len;
+ pid_t pid;
+ struct timeval tv = { 1, 0 }; /* 1 sec */
+
+ 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);
+
+ msg = calloc(1, sizeof(struct resourced_noti));
+ if (msg == NULL) {
+ _E("proc_noti_cb : Not enough memory");
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ client_len = sizeof(client_address);
+ client_sockfd =
+ accept(fd, (struct sockaddr *)&client_address,
+ (socklen_t *)&client_len);
+ if (client_sockfd == -1) {
+ _E("socket accept error");
+ free(msg);
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ ret = setsockopt(client_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret)
+ _E("failed to set socket option");
+ error_code = read_message(client_sockfd, msg);
+
+ if (error_code && _fatal_read_message_error(error_code)) {
+ free_message(msg);
+ close(client_sockfd);
+ return ECORE_CALLBACK_CANCEL;
+ } else if (error_code) { /* It's not fatal */
+ _E("%s : recv error msg, %d", __func__, error_code);
+ safe_write_int(client_sockfd, msg->type, &ret);
+ goto proc_noti_renew;
+ }
+
+ if (msg->argc > NOTI_MAXARG) {
+ _E("%s : error argument", __func__);
+ safe_write_int(client_sockfd, msg->type, &ret);
+ goto proc_noti_renew;
+ }
+
+ if (msg->type >= PROC_CGROUP_GET_CMDLINE) {
+ pid = atoi(msg->argv[0]);
+ send_len = atoi(msg->argv[1]);
+ send_buffer = calloc(1, send_len);
+ if (!send_buffer) {
+ _E("not enough memory for calloc");
+ ret = -ENOMEM;
+ safe_write_int(client_sockfd, msg->type, &ret);
+ goto proc_noti_renew;
+ }
+ ret = proc_get_state(msg->type, pid, send_buffer, send_len);
+ write_response(&ret, client_sockfd, send_buffer, send_len);
+ } else {
+ ret = process_message(msg);
+ safe_write_int(client_sockfd, msg->type, &ret);
+ }
+
+proc_noti_renew:
+
+ close(client_sockfd);
+ free_message(msg);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static int proc_noti_socket_init(void)
+{
+ int fd, n = 0;
+ struct sockaddr_un serveraddr;
+
+ n = sd_listen_fds(0);
+ if (n > 1) {
+ _E("Error: Too many file descriptors received: %d", n);
+ return -1;
+ } else if (n==1) {
+ fd = SD_LISTEN_FDS_START + 0;
+ } else {
+ if (access(RESOURCED_SOCKET_PATH, F_OK) == 0)
+ unlink(RESOURCED_SOCKET_PATH);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ _E("%s: socket create failed\n", __func__);
+ return -1;
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPOUT", "@", 2, 0)) < 0) {
+ _E("%s: Socket SMACK labeling failed\n", __func__);
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPIN", "*", 2, 0)) < 0) {
+ _E("%s: Socket SMACK labeling failed\n", __func__);
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ bzero(&serveraddr, sizeof(struct sockaddr_un));
+ serveraddr.sun_family = AF_UNIX;
+ strncpy(serveraddr.sun_path, RESOURCED_SOCKET_PATH,
+ sizeof(serveraddr.sun_path)-1);
+ serveraddr.sun_path[sizeof(serveraddr.sun_path)-1] = 0;
+
+ if (bind(fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)) <
+ 0) {
+ _E("%s: socket bind failed\n", __func__);
+ close(fd);
+ return -1;
+ }
+
+ if (chmod(RESOURCED_SOCKET_PATH, (S_IRWXU | S_IRWXG | S_IRWXO)) < 0)
+ _E("failed to change the socket permission");
+
+ if (listen(fd, 5) < 0) {
+ _E("%s: socket listen failed\n", __func__);
+ close(fd);
+ return -1;
+ }
+ }
+ _D("socket create & listen ok\n");
+
+ return fd;
+}
+
+static int proc_noti_init(void *data)
+{
+ noti_fd = proc_noti_socket_init();
+ ecore_main_fd_handler_add(noti_fd, ECORE_FD_READ, (Ecore_Fd_Cb)proc_noti_cb,
+ NULL, NULL, NULL);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_noti_exit(void *data)
+{
+ close(noti_fd);
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct proc_module_ops proc_noti_ops = {
+ .name = "PROC_NOTI",
+ .init = proc_noti_init,
+ .exit = proc_noti_exit,
+};
+PROC_MODULE_REGISTER(&proc_noti_ops)
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <Ecore.h>
+
+#include "freezer.h"
+#include "resourced.h"
+#include "trace.h"
+#include "proc-main.h"
+#include "cgroup.h"
+#include "proc-process.h"
+#include "procfs.h"
+#include "lowmem-common.h"
+#include "macro.h"
+#include "proc-noti.h"
+#include "notifier.h"
+#include "proc-appusage.h"
+
+#define PROC_SWEEP_TIMER 3
+static GHashTable *proc_sweep_list;
+static Ecore_Timer *proc_sweep_timer;
+
+enum proc_background_type {
+ PROC_BACKGROUND_INACTIVE,
+ PROC_BACKGROUND_ACTIVE,
+};
+
+int proc_set_service_oomscore(const pid_t pid, const int oom_score)
+{
+ 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 void proc_set_oom_score_childs(GSList *childs, int oom_score_adj)
+{
+ GSList *iter;
+
+ if (!childs)
+ return;
+
+ gslist_for_each_item(iter, childs) {
+ struct child_pid *child = (struct child_pid *)(iter->data);
+ proc_set_oom_score_adj(child->pid, oom_score_adj);
+ }
+}
+
+static void proc_set_oom_score_services(int state, GSList *svcs,
+ int oom_score_adj)
+{
+ GSList *iter;
+
+ if (!svcs)
+ return;
+
+ gslist_for_each_item(iter, svcs) {
+ struct proc_app_info *svc = (struct proc_app_info *)(iter->data);
+ svc->state = state;
+ proc_set_service_oomscore(svc->main_pid, oom_score_adj);
+ }
+}
+
+static int proc_backgrd_manage(int currentpid, int active, int oom_score_adj)
+{
+ pid_t pid = -1;
+ int flag = RESOURCED_NOTIFIER_APP_BACKGRD;
+ struct proc_status ps;
+ FILE *fp;
+ char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+ int cur_oom = -1;
+ static int checkprevpid;
+ int freeze_val = resourced_freezer_proc_late_control();
+ GSList *iter;
+ struct proc_program_info *ppi;
+ struct proc_app_info *pai = find_app_info(currentpid);
+
+ if (!pai || pai->proc_exclude) {
+ _D("BACKGRD MANAGE : don't manage background application by %d", currentpid);
+ return RESOURCED_ERROR_NONFREEZABLE;
+ }
+
+ /*
+ * About groupd process with multiple applications,
+ * all application with same group could be went to background state.
+ * If one application has already managed to background application
+ * it skipped to go to the background again.
+ */
+ if (pai->lru_state >= PROC_BACKGROUND) {
+ _D("already managed background application(%d)", currentpid);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ ps.pid = currentpid;
+ ps.appid = pai->appid;
+ ps.pai = pai;
+ if (active)
+ flag = RESOURCED_NOTIFIER_APP_BACKGRD_ACTIVE;
+ resourced_notify(flag, &ps);
+
+ if (active)
+ goto set_oom;
+
+ if (checkprevpid != currentpid) {
+ gslist_for_each_item(iter, proc_app_list) {
+ struct proc_app_info *spi = (struct proc_app_info *)iter->data;
+ int new_oom;
+ int lru_offset = freeze_val;
+
+ if (!spi->main_pid || spi->type != PROC_TYPE_GUI)
+ continue;
+
+ 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 (spi->lru_state == PROC_BACKGROUND) {
+ memset(&ps, 0, sizeof(struct proc_status));
+ ps.pai = spi;
+ ps.pid = pid;
+ resourced_notify(
+ RESOURCED_NOTIFIER_APP_SUSPEND_READY,
+ &ps);
+ }
+ /*
+ * clear lru offset if platform controls background application
+ */
+ if (spi->flags & PROC_BGCTRL_PLATFORM)
+ lru_offset = 0;
+
+ if (proc_check_lru_suspend(lru_offset, spi->lru_state) &&
+ (proc_check_suspend_state(spi) == PROC_STATE_SUSPEND)) {
+ memset(&ps, 0, sizeof(struct proc_status));
+ ps.pai = spi;
+ ps.pid = pid;
+ resourced_notify(
+ RESOURCED_NOTIFIER_APP_SUSPEND,
+ &ps);
+ }
+
+ if (spi->lru_state >= PROC_BACKGROUND) {
+ spi->lru_state++;
+ if (spi->lru_state > PROC_LRU_MAX )
+ spi->lru_state = PROC_LRU_MAX;
+ _D("BACKGRD : process %d increase lru %d", pid, spi->lru_state);
+ }
+
+ if (cur_oom >= OOMADJ_APP_MAX) {
+ fclose(fp);
+ continue;
+ } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) {
+ new_oom = cur_oom + OOMADJ_APP_INCREASE;
+ _D("BACKGRD : process %d set score %d (before %d)",
+ pid, new_oom, cur_oom);
+ proc_set_oom_score_adj(pid, new_oom);
+ proc_set_oom_score_childs(spi->childs, new_oom);
+ }
+ fclose(fp);
+ }
+ }
+
+ pai->lru_state = PROC_BACKGROUND;
+ if (proc_check_favorite_app(pai->appid)) {
+ _D("detect favorite application : %s", pai->appid);
+ oom_score_adj = OOMADJ_FAVORITE;
+ }
+
+set_oom:
+ proc_set_oom_score_adj(pai->main_pid, oom_score_adj);
+
+ /* change oom score about child pids */
+ proc_set_oom_score_childs(pai->childs, oom_score_adj);
+
+ /* change oom score about grouped service processes */
+ ppi = pai->program;
+ if (ppi && proc_get_svc_state(ppi) == PROC_STATE_BACKGROUND)
+ proc_set_oom_score_services(PROC_STATE_BACKGROUND, ppi->svc_list,
+ oom_score_adj);
+
+ checkprevpid = currentpid;
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_foregrd_manage(int pid, int oom_score_adj)
+{
+ int ret = 0;
+ struct proc_program_info *ppi;
+ struct proc_app_info *pai;
+
+ pai = find_app_info(pid);
+ if (!pai) {
+ proc_set_oom_score_adj(pid, oom_score_adj);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ proc_set_oom_score_adj(pai->main_pid, oom_score_adj);
+
+ /* change oom score about child pids */
+ proc_set_oom_score_childs(pai->childs, oom_score_adj);
+
+ pai->lru_state = PROC_FOREGROUND;
+
+ /* change oom score about grouped service processes */
+ ppi = pai->program;
+ if (ppi)
+ proc_set_oom_score_services(PROC_STATE_FOREGROUND, ppi->svc_list,
+ oom_score_adj);
+
+ return ret;
+}
+
+void proc_kill_victiom(gpointer key, gpointer value, gpointer user_data)
+{
+ int pid = *(gint *)key;
+ int cur_oom = -1;
+ char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+ FILE *fp;
+
+ snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+ fp = fopen(buf, "r+");
+ if (fp == NULL) {
+ _D("sweep proc_kill_victim : background process %d already terminated", pid);
+ return;
+ }
+ if (fgets(buf, sizeof(buf), fp) == NULL) {
+ fclose(fp);
+ return;
+ }
+ cur_oom = atoi(buf);
+ if (cur_oom >= OOMADJ_FAVORITE) {
+ kill(pid, SIGKILL);
+ _D("sweep memory : background process %d killed by sigkill", pid);
+ }
+ fclose(fp);
+}
+
+static Eina_Bool proc_check_sweep_cb(void *data)
+{
+ GHashTable *List = (GHashTable *)data;
+ g_hash_table_foreach(List, proc_kill_victiom, NULL);
+ g_hash_table_destroy(List);
+ proc_sweep_list = NULL;
+ return ECORE_CALLBACK_CANCEL;
+}
+
+int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid)
+{
+ pid_t pid = -1;
+ int count = 0, ret;
+ FILE *fp;
+ gint *piddata;
+ char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+ char appname[PROC_NAME_MAX];
+
+ int cur_oom = -1;
+ int select_sweep_limit;
+ GSList *iter;
+ struct proc_app_info *pai;
+
+ if (proc_sweep_timer)
+ ecore_timer_del(proc_sweep_timer);
+ if (proc_sweep_list)
+ g_hash_table_destroy(proc_sweep_list);
+ proc_sweep_list = g_hash_table_new(g_int_hash, g_int_equal);
+
+ if (type == PROC_SWEEP_EXCLUDE_ACTIVE)
+ select_sweep_limit = OOMADJ_FAVORITE;
+ else
+ select_sweep_limit = OOMADJ_BACKGRD_LOCKED;
+
+ gslist_for_each_item(iter, proc_app_list) {
+ pai = (struct proc_app_info *)iter->data;
+ if (!pai->main_pid || pai->type != PROC_TYPE_GUI)
+ continue;
+
+ pid = pai->main_pid;
+
+ 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);
+ if (cur_oom >= select_sweep_limit) {
+ ret = proc_get_cmdline(pid, appname);
+ if (ret != 0) {
+ fclose(fp);
+ continue;
+ }
+ resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST,
+ pid, NULL, NULL, PROC_TYPE_NONE);
+ piddata = g_new(gint, 1);
+ *piddata = pid;
+ g_hash_table_insert(proc_sweep_list, piddata, NULL);
+ kill(pid, SIGTERM);
+ _D("sweep memory : background process %d(%s) killed",
+ pid, appname);
+ count++;
+ }
+ fclose(fp);
+ }
+ if (count > 0) {
+ proc_sweep_timer =
+ ecore_timer_add(PROC_SWEEP_TIMER, proc_check_sweep_cb, (void *)proc_sweep_list);
+ }
+ return count;
+}
+
+int proc_set_foregrd(pid_t pid, int oom_score_adj)
+{
+ int ret = 0;
+
+ switch (oom_score_adj) {
+ case OOMADJ_FOREGRD_UNLOCKED:
+ case OOMADJ_SU:
+ ret = 0;
+ break;
+ case OOMADJ_FOREGRD_LOCKED:
+ case OOMADJ_BACKGRD_LOCKED:
+ ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_LOCKED);
+ break;
+ case OOMADJ_BACKGRD_UNLOCKED:
+ case OOMADJ_INIT:
+ case OOMADJ_FAVORITE:
+ ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED);
+ break;
+ default:
+ if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+ ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED);
+ } else {
+ ret = -1;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+int proc_set_backgrd(int pid, int oom_score_adj)
+{
+ int ret = 0;
+
+ switch (oom_score_adj) {
+ case OOMADJ_BACKGRD_LOCKED:
+ case OOMADJ_BACKGRD_UNLOCKED:
+ case OOMADJ_SU:
+ ret = -1;
+ break;
+ case OOMADJ_FOREGRD_LOCKED:
+ ret = proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE, OOMADJ_BACKGRD_LOCKED);
+ break;
+ case OOMADJ_FOREGRD_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);
+ break;
+ default:
+ if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ break;
+ }
+ return ret;
+}
+
+int proc_set_active(int pid, int oom_score_adj)
+{
+ int ret = 0;
+ struct proc_app_info *pai;
+
+ switch (oom_score_adj) {
+ case OOMADJ_FOREGRD_LOCKED:
+ case OOMADJ_BACKGRD_LOCKED:
+ case OOMADJ_SU:
+ /* 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;
+ case OOMADJ_BACKGRD_UNLOCKED:
+ pai = find_app_info(pid);
+ if (pai)
+ pai->lru_state = PROC_ACTIVE;
+ ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
+ break;
+ case OOMADJ_PREVIOUS_BACKGRD:
+ ret = proc_set_oom_score_adj(pid, OOMADJ_PREVIOUS_DEFAULT);
+ break;
+ default:
+ if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+ pai = find_app_info(pid);
+ if (pai)
+ pai->lru_state = PROC_ACTIVE;
+ ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
+ } else
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+int proc_set_inactive(int pid, int oom_score_adj)
+{
+ int ret = 0;
+ struct proc_app_info *pai;
+ switch (oom_score_adj) {
+ case OOMADJ_FOREGRD_UNLOCKED:
+ case OOMADJ_BACKGRD_UNLOCKED:
+ case OOMADJ_SU:
+ case OOMADJ_INIT:
+ /* don't change oom value pid */
+ ret = -1;
+ break;
+ case OOMADJ_FOREGRD_LOCKED:
+ ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_UNLOCKED);
+ break;
+ case OOMADJ_BACKGRD_LOCKED:
+ pai = find_app_info(pid);
+ if (pai) {
+ struct proc_status ps = {0};
+ ps.pid = pid;
+ ps.appid = pai->appid;;
+ ps.pai = pai;
+ pai->lru_state = PROC_BACKGROUND;
+ ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
+ resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD, &ps);
+ }
+ break;
+ default:
+ if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ break;
+
+ }
+ return ret;
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Library for getting process statistics
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "macro.h"
+#include "util.h"
+#include "proc_stat.h"
+#include "procfs.h"
+#include "trace.h"
+#include "proc-noti.h"
+#include "proc-info.h"
+#include "const.h"
+
+#define PROC_STAT_PATH "/proc/%d/stat"
+#define PROC_STATM_PATH "/proc/%d/statm"
+#define PROC_CMDLINE_PATH "/proc/%d/cmdline"
+#define PROC_MEMINFO_BUF_SIZE 1024
+
+#ifndef TEST_IN_X86
+#include <assert.h>
+#else
+#define assert(x) \
+do { \
+ if (!(x)) { \
+ printf("assertion %s %d\n", __FILE__ , __LINE__); \
+ exit(-1); \
+ } \
+} while (0)
+#endif
+
+API bool proc_stat_get_cpu_time_by_pid(pid_t pid, unsigned long *utime,
+ unsigned long *stime)
+{
+ char proc_path[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
+ FILE *fp;
+
+ assert(utime != NULL);
+ assert(stime != NULL);
+
+ snprintf(proc_path, sizeof(proc_path), PROC_STAT_PATH, pid);
+ fp = fopen(proc_path, "r");
+ if (fp == NULL)
+ return false;
+
+ if (fscanf(fp, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s") < 0) {
+ fclose(fp);
+ return false;
+ }
+
+ if (fscanf(fp, "%lu %lu", utime, stime) < 1) {
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ return true;
+}
+
+
+API bool proc_stat_get_mem_usage_by_pid(pid_t pid, unsigned int *rss)
+{
+ FILE *fp;
+ char proc_path[sizeof(PROC_STATM_PATH) + MAX_DEC_SIZE(int)] = {0};
+
+ snprintf(proc_path, sizeof(proc_path), PROC_STATM_PATH, 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;
+}
+
+API bool proc_stat_get_total_mem_size(unsigned int *total_mem)
+{
+ static unsigned int total_mem_cached = 0;
+ struct meminfo mi;
+
+ assert(total_mem != NULL);
+
+ if (total_mem_cached)
+ goto finish;
+
+ if (proc_get_meminfo(&mi, MEMINFO_MASK_MEM_TOTAL) < 0)
+ return false;
+
+ total_mem_cached = KBYTE_TO_MBYTE(mi.value[MEMINFO_ID_MEM_TOTAL]);
+
+finish:
+ *total_mem = total_mem_cached;
+
+ return true;
+}
+
+
+API bool proc_stat_get_free_mem_size(unsigned int *free_mem)
+{
+ *free_mem = proc_get_mem_available();
+ return !!(*free_mem);
+}
+
+static bool get_proc_cmdline(pid_t pid, char *cmdline)
+{
+ assert(cmdline != NULL);
+
+ char buf[PATH_MAX];
+ char cmdline_path[sizeof(PROC_CMDLINE_PATH) + MAX_DEC_SIZE(int)] = {0};
+ char *filename;
+ FILE *fp;
+
+ snprintf(cmdline_path, sizeof(cmdline_path), PROC_CMDLINE_PATH, pid);
+ fp = fopen(cmdline_path, "r");
+ if (fp == NULL)
+ return false;
+
+ if (fscanf(fp, "%s", buf) < 1) {
+ fclose(fp);
+ return false;
+ }
+ fclose(fp);
+
+
+ filename = strrchr(buf, '/');
+ if (filename == NULL)
+ filename = buf;
+ else
+ filename = filename + 1;
+
+ strncpy(cmdline, filename, NAME_MAX-1);
+ cmdline[NAME_MAX-1] = 0;
+
+ return true;
+}
+
+static bool get_proc_filename(pid_t pid, char *process_name)
+{
+ FILE *fp;
+ char buf[sizeof(PROC_STAT_PATH) + MAX_DEC_SIZE(int)];
+ char filename[PATH_MAX];
+
+ assert(process_name != NULL);
+
+ snprintf(buf, sizeof(buf), PROC_STAT_PATH, pid);
+ fp = fopen(buf, "r");
+
+ if (fp == NULL)
+ return false;
+
+ if (fscanf(fp, "%*s (%[^)]", filename) < 1) {
+ fclose(fp);
+ return false;
+ }
+
+ strncpy(process_name, filename, NAME_MAX-1);
+ process_name[NAME_MAX-1] = 0;
+ fclose(fp);
+
+ return true;
+}
+
+API bool proc_stat_get_name_by_pid(pid_t pid, char *name)
+{
+
+ assert(name != NULL);
+
+ if (get_proc_cmdline(pid, name))
+ return true;
+ else if (get_proc_filename(pid, name))
+ return true;
+
+ return false;
+}
+
+
+static void diff_system_time(proc_stat_system_time *st_diff, proc_stat_system_time *st_a, proc_stat_system_time *st_b)
+{
+ assert(st_diff != NULL);
+ assert(st_a != NULL);
+ assert(st_b != NULL);
+
+ st_diff->total_time = st_a->total_time - st_b->total_time;
+ st_diff->user_time = st_a->user_time - st_b->user_time;
+ st_diff->nice_time = st_a->nice_time - st_b->nice_time;
+ st_diff->system_time = st_a->system_time - st_b->system_time;
+ st_diff->idle_time = st_a->idle_time - st_b->idle_time;
+ st_diff->iowait_time = st_a->iowait_time - st_b->iowait_time;
+ st_diff->irq_time = st_a->irq_time - st_b->irq_time;
+ st_diff->softirq_time = st_a->softirq_time - st_b->softirq_time;
+}
+
+static bool get_system_time(proc_stat_system_time *st)
+{
+ FILE *fp;
+
+ assert(st != NULL);
+
+ fp = fopen("/proc/stat", "r");
+ if (fp == NULL)
+ return false;
+
+ if (fscanf(fp, "%*s %lld %lld %lld %lld %lld %lld %lld",
+ &st->user_time, &st->nice_time, &st->system_time, &st->idle_time,
+ &st->iowait_time, &st->irq_time, &st->softirq_time) < 1) {
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ st->total_time = st->user_time + st->nice_time + st->system_time + st->idle_time
+ + st->iowait_time + st->irq_time + st->softirq_time;
+
+ return true;
+}
+
+
+
+API bool proc_stat_get_system_time_diff(proc_stat_system_time *st_diff)
+{
+ static proc_stat_system_time prev_st;
+ proc_stat_system_time cur_st;
+
+ assert(st_diff != NULL);
+
+ get_system_time(&cur_st);
+
+ if (prev_st.total_time == 0) {
+ memset(st_diff, 0, sizeof(proc_stat_system_time));
+ prev_st = cur_st;
+ return false;
+ }
+
+ diff_system_time(st_diff, &cur_st, &prev_st);
+ prev_st = cur_st;
+
+ return (bool) (st_diff->total_time);
+}
+
+
+static int comapre_pid(const pid_t *pid_a, const pid_t *pid_b)
+{
+ assert(pid_a != NULL);
+ assert(pid_b != NULL);
+
+ /* the process which has smaller number of pid is ordered ahead */
+ return *pid_a - *pid_b;
+}
+
+/**
+ * @brief Get pids under /proc file system
+ *
+ * @param pids the pointer of GArray to store pids
+ * @return true on success, false on failure.
+ *
+ * This function fills Garray instance with pids under /proc file system.
+ */
+
+static bool get_pids(GArray *pids)
+{
+ DIR *dirp;
+ struct dirent entry;
+ struct dirent *result;
+ int ret;
+
+ assert(pids != NULL);
+
+ dirp = opendir("/proc");
+
+ if (dirp == NULL)
+ return false;
+
+ while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
+ const char *p = entry.d_name;
+ char *end;
+ pid_t pid;
+
+ while (*p) {
+ if (*p < '0' || *p > '9')
+ break;
+ p++;
+ }
+
+ if (*p != 0)
+ continue;
+
+ pid = strtol(entry.d_name, &end, 10);
+
+ g_array_append_val(pids, pid);
+ }
+ closedir(dirp);
+
+ if (ret)
+ return false;
+
+ g_array_sort(pids, (GCompareFunc)comapre_pid);
+
+ return true;
+}
+
+API bool proc_stat_get_pids(pid_t **pids, int *cnt)
+{
+ unsigned int i;
+ GArray *garray = NULL;
+
+ assert(pids != NULL);
+ assert(cnt != NULL);
+
+ garray = g_array_new(false, false, sizeof(pid_t));
+ g_return_val_if_fail(garray, false);
+
+ if (get_pids(garray) == false) {
+ /* g_array_free is resistant to input NULL */
+ g_array_free(garray, true);
+ return false;
+ }
+
+ *pids = malloc(sizeof(pid_t) * garray->len);
+ assert(*pids != NULL);
+
+ *cnt = garray->len;
+
+ for (i = 0; i < garray->len; ++i)
+ (*pids)[i] = g_array_index(garray, pid_t, i);
+
+ g_array_free(garray, true);
+
+ return true;
+}
+
+
+/**
+ * @brief Fill proc_infos with proc_stat_process_info instances which have process statistics , specially time difference each process spent in user mode and system mode between two consecutive its calls
+ *
+ * @param pids GArray instance which have current pids under /proc file system
+ * @param proc_infos the pointer of GArray instance to be filled with proc_stat_process_info instances which are matched with each pid in pids.
+ * @param terminated_proc_infos the pointer of GArray instance to be filled with proc_stat_process_info instances which were terminated between two consecutive its calls
+ ,pass NULL if if this information is not necessary
+ * @return nothing
+ *
+ * This function fills proc_infos with proc_stat_process_info instances which have process statistics , specially time difference each process spent in user mode and system mode between two consecutive its calls
+ *
+ */
+
+static void update_proc_infos(GArray *pids, GArray *proc_infos,
+ GArray *terminated_proc_infos)
+{
+ /* when this function is called first, we don't have basis
+ * for time interval, so it is not valid.
+ */
+ static bool first = true;
+
+ unsigned int pids_cnt = 0;
+ unsigned int cur_pi_idx = 0;
+ proc_stat_process_info *pi = NULL;
+
+ unsigned int i;
+
+ assert(pids != NULL);
+ assert(proc_infos != NULL);
+
+ pids_cnt = pids->len;
+
+ /* with current pids, update proc_infos */
+ for (i = 0; i < pids_cnt; ++i) {
+ unsigned long utime, stime;
+
+ if (cur_pi_idx < proc_infos->len)
+ pi = &g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
+ else
+ /* current pid is out of proc_infos, so it is new pid. */
+ pi = NULL;
+
+ assert(i < pids->len);
+ pid_t pid = g_array_index(pids, pid_t, i);
+
+ if ((pi != NULL) && (pi->pid == pid)) {
+ /* current pid is matched with proc_infos[curPsIdex],
+ * so update proc_infos[curPsIdex]
+ */
+ ++cur_pi_idx;
+
+ pi->fresh = false; /* it is not new process */
+ /* by now, we don't know whether it is valid or not,
+ * so mark it as invalid by default.
+ */
+ pi->valid = false;
+
+ if (!(proc_stat_get_cpu_time_by_pid(pid, &utime, &stime) && proc_stat_get_mem_usage_by_pid(pid, &(pi->rss))))
+ continue;
+
+ if ((pi->utime_prev == utime) && (pi->stime_prev == stime)) {
+ /* There is no diff in execution time, mark it as inactive. */
+ pi->utime_diff = 0;
+ pi->stime_diff = 0;
+ pi->active = false;
+ continue;
+ } else {
+ pi->active = true; /* mark it as active */
+ }
+ /* update time related fields */
+ pi->utime_diff = (utime - pi->utime_prev);
+ pi->stime_diff = (stime - pi->stime_prev);
+ pi->utime_prev = utime;
+ pi->stime_prev = stime;
+
+ pi->valid = true; /* mark it as valid */
+ } else if ((pi == NULL) || (pi->pid > pid)) {
+ /* in case of new process */
+ proc_stat_process_info new_pi;
+
+ new_pi.pid = pid;
+
+ if (!(proc_stat_get_name_by_pid(pid, new_pi.name) && proc_stat_get_cpu_time_by_pid(pid, &utime, &stime) && proc_stat_get_mem_usage_by_pid(pid, &new_pi.rss)))
+ continue; /* in case of not getting information of current pid, skip it */
+
+ new_pi.fresh = true; /* mark it as new (process) */
+ new_pi.utime_prev = utime;
+ new_pi.stime_prev = stime;
+ new_pi.utime_diff = utime;
+ new_pi.stime_diff = stime;
+
+ if (first == false)
+ /* This process is created after the first call of update_proc_infos, so we know execution time of it.
+ * Mark it as valid.
+ */
+ new_pi.valid = true;
+ else
+ new_pi.valid = false;
+
+ /* add it to proc_infos */
+ g_array_insert_val(proc_infos, cur_pi_idx , new_pi);
+ ++cur_pi_idx;
+ } else {
+ if (terminated_proc_infos != NULL) {
+ proc_stat_process_info terminated_pi;
+ terminated_pi = *pi;
+ g_array_append_val(terminated_proc_infos, terminated_pi);
+ }
+
+ /* in case of the process terminated, remove it from proc_infos */
+ assert(cur_pi_idx < proc_infos->len);
+ g_array_remove_index(proc_infos, cur_pi_idx);
+ /* current pid should be compared again, so decrease loop count */
+ --i;
+ }
+
+ }
+
+ /* in case of the process terminated, remove it from proc_infos */
+ while (cur_pi_idx < proc_infos->len) {
+ if (terminated_proc_infos != NULL) {
+ proc_stat_process_info terminated_pi;
+
+ assert(cur_pi_idx < proc_infos->len);
+ terminated_pi = g_array_index(proc_infos, proc_stat_process_info, cur_pi_idx);
+ g_array_append_val(terminated_proc_infos, terminated_pi);
+ }
+
+ assert(cur_pi_idx < proc_infos->len);
+ g_array_remove_index(proc_infos, cur_pi_idx);
+ }
+
+ first = false;
+}
+
+
+
+static int compare_proc_info(const proc_stat_process_info *proc_info_a, const proc_stat_process_info *proc_info_b)
+{
+ /*
+ * Firstly, long execution time process is ordered ahead
+ * Secondly, newly created process is ordered ahead
+ */
+ unsigned long exec_time_a, exec_time_b;
+
+ assert(proc_info_a != NULL);
+ assert(proc_info_b != NULL);
+
+ exec_time_a = proc_info_a->utime_diff + proc_info_a->stime_diff;
+ exec_time_b = proc_info_b->utime_diff + proc_info_b->stime_diff;
+
+ if (exec_time_a != exec_time_b)
+ return exec_time_b - exec_time_a;
+
+ if (proc_info_a->fresh != proc_info_b->fresh)
+ return (int)(proc_info_b->fresh) - (int)(proc_info_a->fresh);
+
+ return 0;
+
+}
+
+
+/**
+ * @brief Extract valid proc_stat_process_info instances from proc_infos and fill valid_proc_infos with these instances
+ *
+ * @param proc_infos from which source to extract valid proc_stat_process_info instances
+ * @param valid_proc_infos GArray instance to be filled with valid proc_stat_process_info instances
+ * @param total_valid_proc_time to get the sum of the time spent by all valid proc_stat_process_info instance, pass NULL if if this information is not necessary
+ * @return nothing
+ *
+ * This function extracts valid proc_stat_process_info instances from proc_infos and fills valid_proc_infos with these instances
+ */
+static void pick_valid_proc_infos(GArray *proc_infos, GArray *valid_proc_infos, unsigned long *total_valid_proc_time)
+{
+ unsigned int i;
+ proc_stat_process_info pi;
+
+ assert(valid_proc_infos != NULL);
+
+ if (total_valid_proc_time != NULL)
+ *total_valid_proc_time = 0;
+
+ for (i = 0; i < proc_infos->len; ++i) {
+ assert(i < proc_infos->len);
+ pi = g_array_index(proc_infos, proc_stat_process_info, i);
+
+ if (pi.valid) {
+ g_array_append_val(valid_proc_infos, pi);
+
+ if (total_valid_proc_time != NULL)
+ *total_valid_proc_time += (pi.utime_diff+pi.stime_diff);
+ }
+ }
+
+ g_array_sort(valid_proc_infos, (GCompareFunc)compare_proc_info);
+}
+
+static GArray *g_pids;
+static GArray *proc_infos;
+
+API void proc_stat_init(void)
+{
+ g_pids = g_array_new(false, false, sizeof(pid_t));
+ proc_infos = g_array_new(false, false, sizeof(proc_stat_process_info));
+}
+
+API bool proc_stat_get_process_info(GArray *valid_proc_infos,
+ GArray *terminated_proc_infos,
+ unsigned long *total_valid_proc_time)
+{
+ assert(valid_proc_infos != NULL);
+
+ g_array_set_size(g_pids, 0);
+
+ if (!get_pids(g_pids))
+ return false;
+
+ update_proc_infos(g_pids, proc_infos, terminated_proc_infos);
+ pick_valid_proc_infos(proc_infos, valid_proc_infos, total_valid_proc_time);
+
+ return true;
+
+}
+
+API void proc_stat_finalize(void)
+{
+ if (g_pids) {
+ g_array_free(g_pids, true);
+ g_pids = NULL;
+ }
+
+ if (proc_infos) {
+ g_array_free(proc_infos, true);
+ proc_infos = NULL;
+ }
+}
+
+
+API unsigned int proc_stat_get_gpu_clock(void)
+{
+ FILE *fp;
+ unsigned int clock;
+
+ fp = fopen("/sys/module/mali/parameters/mali_gpu_clk", "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fscanf(fp, "%d", &clock) < 1) {
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return clock;
+}
+
+bool proc_stat_is_gpu_on(void)
+{
+ if (proc_stat_get_gpu_clock() <= 0)
+ return false;
+
+ return true;
+}
+
+
+
+static inline int send_int(int fd, int val)
+{
+ return write(fd, &val, sizeof(int));
+}
+
+static inline int send_str(int fd, char *str)
+{
+ int len;
+ int ret;
+ if (str == NULL) {
+ len = 0;
+ ret = write(fd, &len, sizeof(int));
+ } else {
+ len = strlen(str);
+ if (len > NOTI_MAXARGLEN)
+ len = NOTI_MAXARGLEN;
+ ret = write(fd, &len, sizeof(int));
+ if (ret < 0) {
+ _E("%s: write failed\n", __func__);
+ return ret;
+ }
+ ret = write(fd, str, len);
+ }
+ return ret;
+}
+
+static int send_socket(struct resourced_noti *msg, bool sync)
+{
+ int client_len;
+ int client_sockfd;
+ int result = 0;
+ struct sockaddr_un clientaddr;
+ int i;
+ int ret = 0;
+ struct timeval tv = { 1, 0 }; /* 1sec */
+
+ client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (client_sockfd == -1) {
+ _E("%s: socket create failed\n", __func__);
+ return -1;
+ }
+
+ bzero(&clientaddr, sizeof(clientaddr));
+ clientaddr.sun_family = AF_UNIX;
+ strncpy(clientaddr.sun_path, RESOURCED_SOCKET_PATH, sizeof(clientaddr.sun_path) - 1);
+ clientaddr.sun_path[sizeof(clientaddr.sun_path)-1] = 0;
+ client_len = sizeof(clientaddr);
+
+ if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) <
+ 0) {
+ _E("%s: connect failed\n", __func__);
+ goto error;
+ }
+
+ ret = setsockopt(client_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret)
+ _E("failed to set socket option");
+ ret = send_int(client_sockfd, msg->pid);
+ if (ret < 0) {
+ _E("send failed (%d)\n", ret);
+ goto error;
+ }
+ ret = send_int(client_sockfd, msg->type);
+ if (ret < 0) {
+ _E("send failed (%d)\n", ret);
+ goto error;
+ }
+ ret = send_int(client_sockfd, msg->argc);
+ if (ret < 0) {
+ _E("send failed (%d)\n", ret);
+ goto error;
+ }
+ for (i = 0; i < msg->argc; i++) {
+ ret = send_str(client_sockfd, msg->argv[i]);
+ if (ret < 0) {
+ _E("send failed (%d)\n", ret);
+ goto error;
+ }
+ }
+
+ if (sync) {
+ ret = read(client_sockfd, &result, sizeof(int));
+ if (ret < 0) {
+ _E("%s: read failed\n", __func__);
+ goto error;
+ }
+ }
+
+ close(client_sockfd);
+ return result;
+error:
+ close(client_sockfd);
+ return ret;
+}
+
+static int send_socket_with_repy(struct resourced_noti *msg,
+ char*buf, char* len_buf)
+{
+ int client_len;
+ int client_sockfd;
+ int result = 0;
+ struct sockaddr_un clientaddr;
+ int i, ret;
+ int size = atoi(len_buf);
+ char errbuf[256];
+ struct timeval tv = { 1, 0 }; /* 1sec */
+
+ client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (client_sockfd == -1) {
+ _E("socket create failed, errno: %d, %s\n", errno, strerror_r(errno, errbuf, sizeof(errbuf)));
+ return -errno;
+ }
+
+ bzero(&clientaddr, sizeof(clientaddr));
+ clientaddr.sun_family = AF_UNIX;
+ strncpy(clientaddr.sun_path, RESOURCED_PROC_INFO_SOCKET_PATH,
+ sizeof(clientaddr.sun_path) - 1);
+ client_len = sizeof(clientaddr);
+
+ ret = connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len);
+ if (ret < 0) {
+ _E("%s: connect failed\n", __func__);
+ close(client_sockfd);
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = setsockopt(client_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret)
+ _E("failed to set socket option");
+ send_int(client_sockfd, msg->pid);
+ send_int(client_sockfd, msg->type);
+ send_int(client_sockfd, msg->argc);
+ for (i = 0; i < msg->argc; i++)
+ send_str(client_sockfd, msg->argv[i]);
+
+ ret = recv(client_sockfd, &result, sizeof(int), 0);
+ if (ret < 0 || result < 0) {
+ _E("%s: read failed\n", __func__);
+ close(client_sockfd);
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret = recv(client_sockfd, buf, size, 0);
+ if (ret < 0) {
+ _E("%s: read failed\n", __func__);
+ close(client_sockfd);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ close(client_sockfd);
+ return result;
+}
+
+static resourced_ret_c proc_cgroup_send_status(const int type, int num, ...)
+{
+ struct resourced_noti *msg;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+ va_list argptr;
+
+ int i;
+ char *args = NULL;
+ bool sync = SYNC_OPERATION(type);
+
+ msg = malloc(sizeof(struct resourced_noti));
+
+ if (msg == NULL)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ msg->pid = getpid();
+ msg->type = type;
+ msg->argc = num;
+ va_start(argptr, num);
+ /* it's just for debug purpose to test error reporting */
+ for (i = 0; i < num; i++) {
+ args = va_arg(argptr, char *);
+ msg->argv[i] = args;
+ }
+ va_end(argptr);
+
+ ret = send_socket(msg, sync);
+ if (ret < 0)
+ ret = RESOURCED_ERROR_FAIL;
+
+ free(msg);
+
+ return ret;
+}
+
+static resourced_ret_c proc_send_get_status(const int type, char* pid_buf,
+ char*buf, char* len_buf)
+{
+ struct resourced_noti *msg;
+ resourced_ret_c ret = RESOURCED_ERROR_NONE;
+
+ msg = malloc(sizeof(struct resourced_noti));
+
+ if (msg == NULL)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ msg->pid = getpid();
+ msg->type = type;
+ msg->argc = 2;
+ msg->argv[0] = pid_buf;
+ msg->argv[1] = len_buf;
+
+ ret = send_socket_with_repy(msg, buf, len_buf);
+ if (ret < 0)
+ ret = RESOURCED_ERROR_FAIL;
+
+ free(msg);
+ return ret;
+}
+
+
+API resourced_ret_c proc_cgroup_foregrd(void)
+{
+ char buf[MAX_DEC_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", getpid());
+ return proc_cgroup_send_status(PROC_CGROUP_SET_FOREGRD, 1, buf);
+}
+
+API resourced_ret_c proc_cgroup_backgrd(void)
+{
+ char buf[MAX_DEC_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", getpid());
+ return proc_cgroup_send_status(PROC_CGROUP_SET_BACKGRD, 1, buf);
+}
+
+API resourced_ret_c proc_cgroup_active(pid_t pid)
+{
+ char buf[MAX_DEC_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", pid);
+ return proc_cgroup_send_status(PROC_CGROUP_SET_ACTIVE, 1, buf);
+}
+
+API resourced_ret_c proc_cgroup_inactive(pid_t pid)
+{
+ char buf[MAX_DEC_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", pid);
+ return proc_cgroup_send_status(PROC_CGROUP_SET_INACTIVE, 1, buf);
+}
+
+API resourced_ret_c proc_group_change_status(int type, pid_t pid, char *app_id)
+{
+ char pid_buf[MAX_DEC_SIZE(int)];
+ char appid_buf[NOTI_MAXARGLEN];
+ snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
+ snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
+ return proc_cgroup_send_status(type, 2, pid_buf, appid_buf);
+}
+
+API resourced_ret_c proc_cgroup_sweep_memory(void)
+{
+ char buf[MAX_DEC_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", getpid());
+ return proc_cgroup_send_status(PROC_CGROUP_GET_MEMSWEEP, 1, buf);
+}
+
+API resourced_ret_c proc_cgroup_launch(int type, pid_t pid, char *app_id, char *pkg_id)
+{
+ char pid_buf[MAX_DEC_SIZE(int)];
+ char appid_buf[NOTI_MAXARGLEN];
+ char pkgid_buf[NOTI_MAXARGLEN];
+ snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
+ snprintf(appid_buf, sizeof(appid_buf)-1, "%s", app_id);
+ snprintf(pkgid_buf, sizeof(pkgid_buf)-1, "%s", pkg_id);
+ return proc_cgroup_send_status(type, 3, pid_buf, appid_buf, pkgid_buf);
+}
+
+API resourced_ret_c proc_stat_get_pid_entry(int type, pid_t pid,
+ char* buf, int len)
+{
+ char pid_buf[MAX_DEC_SIZE(int)];
+ char len_buf[MAX_DEC_SIZE(int)];
+ snprintf(pid_buf, sizeof(pid_buf), "%d", pid);
+ snprintf(len_buf, sizeof(len_buf), "%d", len);
+ return proc_send_get_status(type, pid_buf, buf, len_buf);
+}
+
--- /dev/null
+/*
+ * 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 "smaps.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)
+{
+ _cleanup_smaps_free_ struct smaps *maps = NULL;
+ int r;
+
+ r = smaps_get(pid, &maps, (SMAPS_MASK_RSS |
+ SMAPS_MASK_PSS |
+ SMAPS_MASK_SHARED_CLEAN |
+ SMAPS_MASK_SHARED_DIRTY |
+ SMAPS_MASK_PRIVATE_CLEAN |
+ SMAPS_MASK_PRIVATE_DIRTY));
+ if (r < 0) {
+ _E("error reading /proc/%d/smaps file", pid);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ mem_info->rss = maps->sum[SMAPS_ID_RSS];
+ mem_info->pss = maps->sum[SMAPS_ID_PSS];
+ mem_info->shared_clean = maps->sum[SMAPS_ID_SHARED_CLEAN];
+ mem_info->shared_dirty = maps->sum[SMAPS_ID_SHARED_DIRTY];
+ mem_info->private_clean = maps->sum[SMAPS_ID_PRIVATE_CLEAN];
+ mem_info->private_dirty = maps->sum[SMAPS_ID_PRIVATE_DIRTY];
+
+ return RESOURCED_ERROR_NONE;
+}
+
+/* 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);
+
+ if (rt_task->pipe_fds[0] >= 0)
+ close(rt_task->pipe_fds[0]);
+ if (rt_task->pipe_fds[1] >= 0)
+ close(rt_task->pipe_fds[1]);
+
+ dbus_message_unref(rt_task->task_msg);
+
+ free(rt_task);
+}
--- /dev/null
+/*
+ * 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-main.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 */
+ if (!rt_task)
+ return ECORE_CALLBACK_CANCEL;
+
+ _D("task %s: error occured in collection of usage info, sending error message", rt_task->task_name);
+
+ ret = -EREMOTEIO;
+ reply = dbus_message_new_method_return(rt_task->task_msg);
+ 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 *)calloc(1, (sizeof(struct runtime_info_task)));
+ if (!rt_task) {
+ _E("out of memory: not able to create runtime_info_task");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ rt_task->usage_info_list = NULL;
+ rt_task->pipe_fds[0] = -1;
+ rt_task->pipe_fds[1] = -1;
+
+ /* 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;
+ }
+
+ 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 */
+ if (rt_task)
+ _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);
+}
+
+static int proc_usage_stats_init(void *data)
+{
+ edbus_add_methods(RESOURCED_PATH_PROCESS, edbus_methods, ARRAY_SIZE(edbus_methods));
+ return RESOURCED_ERROR_NONE;
+}
+
+static int proc_usage_stats_exit(void *data)
+{
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct proc_module_ops proc_usage_stats_ops = {
+ .name = "PROC_USAGE_STATS",
+ .init = proc_usage_stats_init,
+ .exit = proc_usage_stats_exit,
+};
+PROC_MODULE_REGISTER(&proc_usage_stats_ops)
--- /dev/null
+[APPUSAGE]
+APPUSAGE=ON
+# predefined favorite lists
+PREDEFINE=com.samsung.phone
+PREDEFINE=com.samsung.contacts
+PREDEFINE=com.samsung.setting
+PREDEFINE=com.samsung.browser
+PREDEFINE=com.samsung.shealth
\ No newline at end of file
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#ifndef __RESOURCED_DBUS_H__
+#define __RESOURCED_DBUS_H__
+
+#include <stdbool.h>
+#include <dbus/dbus.h>
+#include <E_DBus.h>
+
+E_DBus_Connection *resourced_dbus_monitor_new(DBusBusType type, DBusHandleMessageFunction filter_func, const char * const *filters);
+
+bool resourced_dbus_pid_has_busname(pid_t pid);
+unsigned int resourced_dbus_pid_get_busnames(pid_t pid, char ***busnames);
+pid_t resourced_dbus_get_pid_of_busname(const char *busname);
+
+#endif /* __RESOURCED_DBUS_H__ */
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#include <assert.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <E_DBus.h>
+
+#include "edbus-handler.h"
+#include "macro.h"
+#include "util.h"
+#include "module.h"
+#include "resourced.h"
+#include "trace.h"
+#include "resourced-dbus.h"
+
+/* key : DBus system BusName */
+/* value: PID of Busname */
+/* BusName hash is 1:1 table */
+static GHashTable *dbus_system_busname_hash = NULL;
+
+/* key : PID */
+/* value: Another GHashTable of BusNames */
+/* BusName hash is 1:N table */
+static GHashTable *dbus_system_pid_hash = NULL;
+
+E_DBus_Signal_Handler *dbus_name_owner_changed_handler = NULL;
+
+
+static void ghash_free(void *hash)
+{
+ if (g_hash_table_size((GHashTable *)hash))
+ g_hash_table_remove_all((GHashTable *)hash);
+
+ g_hash_table_unref((GHashTable *)hash);
+}
+
+bool resourced_dbus_pid_has_busname(pid_t pid)
+{
+ return g_hash_table_contains(dbus_system_pid_hash, GUINT_TO_POINTER(pid));
+}
+
+unsigned int resourced_dbus_pid_get_busnames(pid_t pid, char ***busnames)
+{
+ void *pid_p = GUINT_TO_POINTER(pid);
+ GHashTable *busname_hash = NULL;
+ char **names = NULL;
+ unsigned int len = 0;
+
+ assert(busnames);
+
+ busname_hash = g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash)
+ return 0;
+
+#if (GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 40)
+ {
+ GHashTableIter iter;
+ char *busname;
+ int i;
+
+ names = (char **)malloc(sizeof(char *) * (size_t)(g_hash_table_size(busname_hash) + 1));
+ if (!names)
+ return 0;
+
+ g_hash_table_iter_init (&iter, busname_hash);
+ for (i = 0; g_hash_table_iter_next(&iter, (void **)&busname, NULL); i++)
+ names[i] = strndup(busname, strlen(busname)+1);
+
+ names[i] = NULL;
+ }
+#else
+ names = (char **)g_hash_table_get_keys_as_array(busname_hash, &len);
+#endif
+ *busnames = names;
+
+ return len;
+}
+
+pid_t resourced_dbus_get_pid_of_busname(const char *busname)
+{
+ return GPOINTER_TO_UINT(g_hash_table_lookup(dbus_system_busname_hash, busname));
+}
+
+static void resourced_dbus_system_bus_append_connection_info(void *key, void *value, void *data)
+{
+ DBusMessageIter *iter = data, sub;
+ char *busname = key;
+ pid_t pid = GPOINTER_TO_UINT(value);
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub)) {
+ _E("Failed to open dictionary");
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &busname)) {
+ _E("Failed to append string: %s", busname);
+ dbus_message_iter_abandon_container(iter, &sub);
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &pid)) {
+ _E("Failed to append uint32: %u", pid);
+ dbus_message_iter_abandon_container(iter, &sub);
+ return;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub)) {
+ _E("Failed to close array");
+ return;
+ }
+}
+
+static DBusMessage *resourced_dbus_handle_get_system_bus_name_info(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter, sub;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{su}", &sub)) {
+ _E("Failed to open string array");
+ return NULL;
+ }
+
+ g_hash_table_foreach(dbus_system_busname_hash,
+ resourced_dbus_system_bus_append_connection_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+}
+
+static void resourced_dbus_system_bus_append_busname_info(void *key, void *value, void *data)
+{
+ if (!dbus_message_iter_append_basic((DBusMessageIter *)data, DBUS_TYPE_STRING, &key))
+ _E("Failed to append string: %s", (char *)key);
+}
+
+static DBusMessage *resourced_dbus_handle_get_system_bus_pid_info(E_DBus_Object *obj, DBusMessage *msg)
+{
+ GHashTable *busname_hash = NULL;
+ DBusMessageIter iter, sub;
+ DBusMessage *reply = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+ pid_t pid;
+
+ if (!dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) {
+ _E("Failed to get arguments from message: %s", error.message);
+ return NULL;
+ }
+
+ if (!g_hash_table_contains(dbus_system_pid_hash, GUINT_TO_POINTER(pid))) {
+ reply = dbus_message_new_error(msg,
+ DBUS_ERROR_INVALID_ARGS,
+ "Given PID has no system bus");
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ busname_hash = g_hash_table_lookup(dbus_system_pid_hash, GUINT_TO_POINTER(pid));
+ if (!busname_hash) {
+ _E("Failed to find value of key(PID: %u)", pid);
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ _E("Failed to open string array");
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ g_hash_table_foreach(busname_hash,
+ resourced_dbus_system_bus_append_busname_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+}
+
+static void resourced_dbus_system_bus_append_pid_info(void *key, void *value, void *data)
+{
+ DBusMessageIter *iter = data, sub, ssub;
+ pid_t pid = GPOINTER_TO_UINT(key);
+ GHashTable *busname_hash = value;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub)) {
+ _E("Failed to open dictionary");
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &pid)) {
+ _E("Failed to append uint32: %u", pid);
+ return;
+ }
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "s", &ssub)) {
+ _E("Failed to open string array");
+ return;
+ }
+
+ g_hash_table_foreach(busname_hash,
+ resourced_dbus_system_bus_append_busname_info,
+ &ssub);
+
+ if (!dbus_message_iter_close_container(&sub, &ssub)) {
+ _E("Failed to close array");
+ return;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub)) {
+ _E("Failed to close array");
+ return;
+ }
+}
+
+static DBusMessage *resourced_dbus_handle_get_system_bus_pid_info_all(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter, sub;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{uas}", &sub)) {
+ _E("Failed to open string array");
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ g_hash_table_foreach(dbus_system_pid_hash,
+ resourced_dbus_system_bus_append_pid_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+}
+
+static struct edbus_method resourced_dbus_methods[] = {
+ { "GetSystemBusPIDInfo", "u", "as",
+ resourced_dbus_handle_get_system_bus_pid_info },
+ { "GetSystemBusPIDInfoAll", NULL, "a{uas}",
+ resourced_dbus_handle_get_system_bus_pid_info_all },
+ { "GetSystemBusNameInfo", NULL, "a{su}",
+ resourced_dbus_handle_get_system_bus_name_info },
+ { NULL, NULL, NULL,
+ NULL },
+};
+
+static void resourced_dbus_system_hash_insert_busname(char *busname, pid_t pid)
+{
+ GHashTable *busname_hash = NULL;
+ void *pid_p = GUINT_TO_POINTER(pid);
+
+ g_hash_table_replace(dbus_system_busname_hash, busname, pid_p);
+
+ busname_hash = (GHashTable *)g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash) {
+ /* !! CAUTION !! */
+ /* We are using same busname pointer at two of hash
+ * table. So the free operation should be done at
+ * once. */
+ busname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+ if (!busname_hash) {
+ _E("Failed to create BusName hash table");
+ return;
+ }
+ g_hash_table_replace(dbus_system_pid_hash, pid_p, busname_hash);
+ }
+
+ g_hash_table_replace(busname_hash, busname, NULL);
+}
+
+static void resourced_dbus_system_hash_drop_busname(char *busname)
+{
+ GHashTable *busname_hash = NULL;
+ void *pid_p;
+
+ /* BusName hash */
+ if (!g_hash_table_contains(dbus_system_busname_hash, busname)) {
+ _E("Does not exist in busname hash: %s", busname);
+ return;
+ }
+
+ /* Lookup PID of BusName */
+ pid_p = g_hash_table_lookup(dbus_system_busname_hash, busname);
+
+ /* Drop PID hash */
+ if (!g_hash_table_contains(dbus_system_pid_hash, pid_p)) {
+ _E("Does not exist in PID hash: %s", busname);
+ return;
+ }
+
+ busname_hash = (GHashTable *)g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash) {
+ _E("Failed to find value of PID: %u", GPOINTER_TO_UINT(pid_p));
+ g_hash_table_remove(dbus_system_pid_hash, pid_p);
+ return;
+ }
+
+ g_hash_table_remove(busname_hash, busname);
+
+ if (!g_hash_table_size(busname_hash)) {
+ g_hash_table_unref(busname_hash);
+ if (!g_hash_table_remove(dbus_system_pid_hash, pid_p))
+ _E("Failed to drop from PID hash table: %s", busname);
+ }
+
+ /* Drop BusName hash */
+ if (!g_hash_table_remove(dbus_system_busname_hash, busname))
+ _E("Failed to drop from busname hash table: %s", busname);
+}
+
+static void resourced_dbus_get_connection_unix_process_id_callback(void *data, DBusMessage *msg, DBusError *error)
+{
+ DBusError err = DBUS_ERROR_INIT;
+ char *busname = data;
+ pid_t pid;
+
+ if (dbus_error_is_set(error)) {
+ free(busname);
+ return;
+ }
+
+ if (!dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) {
+ free(busname);
+ _E("Failed to get arguments from message: %s", err.message);
+ return;
+ }
+
+ resourced_dbus_system_hash_insert_busname(busname, pid);
+}
+
+static void resourced_dbus_get_connection_unix_process_id(char *busname)
+{
+ E_DBus_Connection *conn = NULL;
+ DBusMessage *msg = NULL;
+ DBusPendingCall *pending = NULL;
+
+ conn = get_resourced_edbus_connection();
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID");
+ if (!msg) {
+ _E("Failed to get new message");
+ return;
+ }
+
+ if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &busname, DBUS_TYPE_INVALID)) {
+ _E("Failed to append args");
+ dbus_message_unref(msg);
+ return;
+ }
+
+ pending = e_dbus_message_send(conn,
+ msg,
+ resourced_dbus_get_connection_unix_process_id_callback,
+ -1,
+ strndup(busname, strlen(busname)+1));
+ if (!pending) {
+ _E("Failed to send message");
+ dbus_message_unref(msg);
+ return;
+ }
+
+ dbus_message_unref(msg);
+
+ return;
+}
+
+static void resourced_dbus_get_list_names_callback(void *data, DBusMessage *msg, DBusError *error)
+{
+ DBusMessageIter iter, sub;
+ char *busname = NULL;
+
+ if (dbus_error_is_set(error)) {
+ _E("Failed to get DBus list names: %s", error->message);
+ return;
+ }
+
+ if (!dbus_message_iter_init(msg, &iter)) {
+ _D("message has no arguments");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ do {
+ dbus_message_iter_get_basic(&sub, &busname);
+ resourced_dbus_get_connection_unix_process_id(busname);
+ } while(dbus_message_iter_next(&sub));
+}
+
+static bool resourced_dbus_get_list_names(void)
+{
+ DBusMessage *msg = NULL;
+ DBusPendingCall *pending = NULL;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "ListNames");
+ if (!msg) {
+ _E("Failed to get new message");
+ return FALSE;
+ }
+
+ pending = e_dbus_message_send(get_resourced_edbus_connection(),
+ msg,
+ resourced_dbus_get_list_names_callback,
+ -1,
+ NULL);
+ if (!pending) {
+ _E("Failed to send message");
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+static bool resourced_dbus_become_monitor(DBusConnection *connection,
+ const char * const *filters)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *msg;
+ DBusMessage *reply;
+ int i;
+ unsigned int zero = 0;
+ DBusMessageIter iter, sub;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_MONITORING,
+ "BecomeMonitor");
+ if (!msg) {
+ _E("Failed to become a monitor");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ _E("Failed to open string array");
+ return FALSE;
+ }
+
+ for (i = 0; filters[i] != NULL; i++) {
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &filters[i])) {
+ _E("Failed to add filter to array");
+ return FALSE;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &zero)) {
+ _E("Failed to append finish argument zero");
+ return FALSE;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error);
+ if (!reply) {
+ _E("Failed to enable new-style monitoring: "
+ "%s: \"%s\". Falling back to eavesdropping.",
+ error.name, error.message);
+ dbus_error_free(&error);
+ return FALSE;
+ }
+
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+
+ return (reply != NULL);
+}
+
+E_DBus_Connection *resourced_dbus_monitor_new(DBusBusType type,
+ DBusHandleMessageFunction filter_func,
+ const char * const *filters)
+{
+ E_DBus_Connection *edbus_conn = NULL;
+ DBusConnection *conn = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+
+ conn = dbus_bus_get_private(type, &error);
+ if (!conn) {
+ _E("Failed to open connecion: %s", error.message);
+ goto on_error;
+ }
+
+ if (!dbus_connection_add_filter(conn,
+ filter_func,
+ NULL,
+ NULL)) {
+ _E("Failed to add filter function on connection");
+ goto on_error;
+ }
+
+ if (!resourced_dbus_become_monitor(conn, filters)) {
+ _E("Failed to become a monitor connection");
+ goto on_error;
+ }
+
+ edbus_conn = e_dbus_connection_setup(conn);
+ if (!edbus_conn) {
+ _E("Failed to setup edbus connection");
+ goto on_error;
+ }
+
+ return edbus_conn;
+
+on_error:
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+
+ if (conn) {
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ }
+
+ return NULL;
+}
+
+static void resourced_dbus_name_owner_changed_callback(void *data, DBusMessage *msg)
+{
+ char *busname = NULL, *from = NULL, *to = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!dbus_message_get_args(msg, &error,
+ DBUS_TYPE_STRING, &busname,
+ DBUS_TYPE_STRING, &from,
+ DBUS_TYPE_STRING, &to,
+ DBUS_TYPE_INVALID)) {
+ _E("Failed to get arguments from message: %s", error.message);
+ return;
+ }
+
+ /* If the name owner process is activated then: */
+ /* arg_0: busname */
+ /* arg_1: "" */
+ /* arg_2: ":x.xxx" */
+
+ /* If the name owner process is deactivated then: */
+ /* arg_0: busname */
+ /* arg_1: ":x.xxx" */
+ /* arg_2: "" */
+
+ if (is_empty(busname)) {
+ _E("NameOwnerChanged: arg_0 is empty");
+ return;
+ }
+
+ if (is_empty(from) && is_empty(to)) {
+ _E("NameOwnerChanged: both arg_1 and arg_2 are empty");
+ return;
+ }
+
+ if (is_empty(from) && !is_empty(to)) {
+ /* New BusName */
+
+ resourced_dbus_get_connection_unix_process_id(busname);
+ return;
+ } else if (!is_empty(from) && is_empty(to)) {
+ /* Drop BusName */
+
+ resourced_dbus_system_hash_drop_busname(busname);
+ return;
+ }
+
+ _E("Should NOT reached here!!");
+}
+
+static int resourced_dbus_init(void *data)
+{
+ resourced_ret_c ret;
+
+ dbus_system_busname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
+ if (!dbus_system_busname_hash)
+ _E("Failed to create dbus connection list hash table");
+
+ dbus_system_pid_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, ghash_free);
+ if (!dbus_system_pid_hash)
+ _E("Failed to create dbus connection list hash table");
+
+ resourced_dbus_get_list_names();
+
+ dbus_name_owner_changed_handler = e_dbus_signal_handler_add(get_resourced_edbus_connection(),
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged",
+ resourced_dbus_name_owner_changed_callback,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_DBUS,
+ resourced_dbus_methods,
+ ARRAY_SIZE(resourced_dbus_methods));
+
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+ "DBus method registration for %s is failed", RESOURCED_PATH_DBUS);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_dbus_finalize(void *data)
+{
+ ghash_free(dbus_system_busname_hash);
+ ghash_free(dbus_system_pid_hash);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops resourced_dbus_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "resourced-dbus",
+ .init = resourced_dbus_init,
+ .exit = resourced_dbus_finalize,
+};
+
+MODULE_REGISTER(&resourced_dbus_modules_ops)
--- /dev/null
+/*
+ * 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 init.c
+ * @desc Resourced initialization
+ *
+ **/
+
+#include "const.h"
+#include "counter.h"
+#include "edbus-handler.h"
+#include "cgroup.h"
+#include "init.h"
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "proc-main.h"
+#include "proc-monitor.h"
+#include "swap-common.h"
+#include "trace.h"
+#include "version.h"
+
+#include <Ecore.h>
+#include <getopt.h>
+#include <signal.h>
+
+static void print_root_usage()
+{
+ puts("You must be root to start it.");
+}
+
+static int assert_root(void)
+{
+ if (getuid() != 0) {
+ print_root_usage();
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void sig_term_handler(int sig)
+{
+ _E("sigterm or sigint received");
+ resourced_quit_mainloop();
+}
+
+static void add_signal_handler(void)
+{
+ signal(SIGTERM, sig_term_handler);
+ signal(SIGINT, sig_term_handler);
+}
+
+static Eina_Bool quit_main_loop(void *user_data)
+{
+ ecore_main_loop_quit();
+ return ECORE_CALLBACK_CANCEL;
+}
+
+int resourced_init(struct daemon_arg *darg)
+{
+ int ret;
+
+ ret_value_msg_if(darg == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+ "Invalid daemon argument\n");
+ ret = assert_root();
+ ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
+ ecore_init();
+ add_signal_handler();
+ edbus_init();
+
+ ret = modules_add_methods();
+ if (ret < 0) {
+ _E("Failed to add resourced module DBus method calls");
+ return ret;
+ }
+
+ /* 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;
+}
+
+int resourced_deinit(void)
+{
+ ecore_shutdown();
+ edbus_exit();
+ return RESOURCED_ERROR_NONE;
+}
+
+void resourced_quit_mainloop(void)
+{
+ static bool resourced_quit;
+
+ if (resourced_quit)
+ return;
+ resourced_quit = true;
+
+ struct shared_modules_data *shared_data = get_shared_modules_data();
+
+ 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);
+#endif
+ }
+ ecore_timer_thaw(shared_data->darg->ecore_quit);
+}
+
+void set_daemon_net_block_state(const enum traffic_restriction_type rst_type,
+ const struct counter_arg *carg)
+{
+ ret_msg_if(carg == NULL,
+ "Please provide valid counter arg!");
+
+ if (rst_type == RST_SET)
+ carg->opts->state |= RESOURCED_NET_BLOCKED_STATE; /* set bit */
+ else {
+ carg->opts->state &=(~RESOURCED_NET_BLOCKED_STATE); /* nulify bit */
+ ecore_timer_thaw(carg->ecore_timer);
+ }
+}
--- /dev/null
+/*
+ * 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 init.h
+ * @desc Resourced initialization
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ **/
+
+#ifndef _RESOURCED_INIT_H
+#define _RESOURCED_INIT_H
+
+#include <Ecore.h>
+
+#include "resourced.h"
+
+#include "transmission.h"
+
+struct daemon_arg {
+ int argc;
+ char **argv;
+ Ecore_Timer *ecore_quit;
+};
+
+int resourced_init(struct daemon_arg *darg);
+
+int resourced_deinit(void);
+
+void resourced_quit_mainloop(void);
+
+struct counter_arg;
+
+void set_daemon_net_block_state(const enum traffic_restriction_type rst_type,
+ const struct counter_arg* carg);
+
+#endif /* _RESOURCED_INIT_H */
--- /dev/null
+/*
+ * 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 main.c
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include "init.h"
+#include "macro.h"
+#include "module-data.h"
+#include "module.h"
+#include "proc-main.h"
+#include "proc-monitor.h"
+#include "trace.h"
+#include "version.h"
+#include "edbus-handler.h"
+#include "notifier.h"
+
+#include <Ecore.h>
+#include <mcheck.h>
+#include <systemd/sd-daemon.h>
+
+int main(int argc, char **argv)
+{
+ int ret_code = 0;
+ struct daemon_arg darg = { argc, argv, NULL };
+
+#ifdef NETWORK_DEBUG_ENABLED
+ mtrace();
+ mcheck(0);
+#endif
+ ret_code = resourced_init(&darg);
+ ret_value_msg_if(ret_code < 0, ret_code,
+ "Resourced initialization failed\n");
+ init_modules_arg(&darg);
+ modules_check_runtime_support(NULL);
+ if (check_dbus_active()) {
+ _I("notify relaunch");
+ modules_init(NULL);
+ resourced_notify(RESOURCED_NOTIFIER_BOOTING_DONE, NULL);
+ } else {
+ _I("lauch resourced at first");
+ modules_early_init(NULL);
+ }
+ sd_notify(0, "READY=1");
+
+ ecore_main_loop_begin();
+ modules_exit(NULL);
+ resourced_deinit();
+ return ret_code;
+}
--- /dev/null
+[Unit]
+Description=Resource management daemon
+After=wm_ready.service
+
+[Service]
+Type=notify
+EnvironmentFile=/run/tizen-mobile-env
+ExecStart=/usr/bin/resourced
+MemoryLimit=30M
+Restart=on-failure
+RestartSec=0
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+[Unit]
+Description=Resourced socket
+
+[Socket]
+ListenStream=/tmp/proc_info
+SocketMode=0777
+SmackLabelIPIn=*
+SmackLabelIPOut=@
--- /dev/null
+/*
+ * 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 sluggish.c
+ * @desc Sluggishness processing functions
+ *
+ **/
+
+#include <glib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "edbus-handler.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "time-helper.h"
+#include "proc-cpu.h"
+#include "storage-helper.h"
+#include "file-helper.h"
+#include "util.h"
+
+#define SLUGGISH_PATH "/opt/usr/share/sluggish" /* Path to dump system snapshot */
+#define PATH_DLOG "/opt/var/log/dlog_main" /* Platform log file */
+#define PATH_DLOG1 "/opt/var/log/dlog_main.1" /* Platform log file */
+#define MAX_TIMESTAMP_LEN 30
+#define MAX_FILENAME_LEN 256
+#define MAX_BUF_LEN 1024
+#define POPUP_KEY_CONTENT "_SYSPOPUP_CONTENT_"
+#define SLUGGISH_POPUP "sluggish_popup"
+#define SLUGGISH_TYPE "_DETECTION_TYPE_"
+#define MEMINFO_FILE_PATH "/proc/meminfo" /* meminfo file path */
+#define VMSTAT_FILE_PATH "/proc/vmstat" /* vmstat file path */
+#define SLUGGISH_DUP_NOTI_TIMELIMIT 5 /* duplicate sluggish notification timelimit */
+#define KILOBYTE 1024
+#define POPUP_DELAY_TIME 15 /* Unit: seconds */
+#define CPU_THRESHOLD 80 /* Delay popup if CPU usage percentage crossed this limit */
+#define GET_BATTERY_CAPACITY "GetPercent"
+
+static int sluggish_count; /* sluggish notification count*/
+
+enum sluggish_event {
+ GRAPHICS = 1, /* Graphics vertical */
+ MULTIMEDIA /* Multimedia vertical */
+};
+
+typedef void (*sluggish_stat_func)(char *ts);
+
+struct sluggish_stat {
+ char *type;
+ sluggish_stat_func func;
+};
+
+static void sluggish_launch_popup(int type)
+{
+ char *param[4];
+ int ret;
+
+ /* Launch sluggish system popup */
+ param[0] = POPUP_KEY_CONTENT;
+ param[1] = SLUGGISH_POPUP;
+ param[2] = SLUGGISH_TYPE;
+ if (type == GRAPHICS)
+ param[3] = "Graphics sluggishenss";
+ else if (type == MULTIMEDIA)
+ param[3] = "Multimedia sluggishens";
+ else {
+ _E("Invalid sluggish type :%d", type);
+ return;
+ }
+ ret = dbus_method_async("org.tizen.system.popup",
+ "/Org/Tizen/System/Popup/System",
+ "org.tizen.system.popup.System",
+ "SluggishPopupLaunch", "ssss", param);
+ if (ret < 0)
+ _E("Failed to launch SluggishPopup");
+ else
+ _I("SluggishPopupLaunch Success");
+}
+
+static gboolean sluggish_popup_cb(gpointer user_data)
+{
+ sluggish_launch_popup((int )user_data);
+ return FALSE;
+}
+
+static void sluggish_get_vmstat(char *timestamp)
+{
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/vmstat", SLUGGISH_PATH, timestamp);
+ /* dump /proc/vmstat to SLUGGISH_PATH */
+ ret = copy_file(file_name, VMSTAT_FILE_PATH);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", VMSTAT_FILE_PATH, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", VMSTAT_FILE_PATH, file_name);
+}
+
+static void sluggish_get_memps(char *timestamp)
+{
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+ char *argv[3];
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/memps", SLUGGISH_PATH, timestamp);
+ argv[0] = "memps";
+ argv[1] = "-a";
+ argv[2] = NULL;
+
+ /* dump memps output to SLUGGISH_PATH */
+ ret = exec_cmd(argv, file_name);
+ if (ret < 0)
+ _E("Cmd: memps -a Failed");
+ else
+ _I("Cmd: memps -a Success");
+}
+
+static void sluggish_get_meminfo(char *timestamp)
+{
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/meminfo", SLUGGISH_PATH, timestamp);
+ /* dump /proc/meminfo to SLUGGISH_PATH */
+ ret = copy_file(file_name, MEMINFO_FILE_PATH);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", MEMINFO_FILE_PATH, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", MEMINFO_FILE_PATH, file_name);
+}
+
+static void sluggish_get_psinfo(char *timestamp)
+{
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+ char *argv[4];
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/ps-eo", SLUGGISH_PATH, timestamp);
+ argv[0] = "ps";
+ argv[1] = "-eo";
+ argv[2] = "pid,uname,ppid,pri,ni,vsize,rss,pcpu,pmem,size,time,s,policy,cmd";
+ argv[3] = NULL;
+
+ /* dump ps -eo output to SLUGGISH_PATH */
+ ret = exec_cmd(argv, file_name);
+ if (ret < 0)
+ _E("Cmd: ps -eo Failed");
+ else
+ _I("Cmd: ps -eo Success");
+}
+
+static const struct sluggish_stat sluggish_memstat[] = {
+ { "vmstat", sluggish_get_vmstat },
+ { "memps", sluggish_get_memps },
+ { "meminfo", sluggish_get_meminfo },
+};
+
+static void sluggish_get_mem_status(char *timestamp)
+{
+ /* Get memory status using vmstat, meminfo, memps and dump to SLUGGISH_PATH */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sluggish_memstat); i++) {
+ sluggish_memstat[i].func(timestamp);
+ }
+}
+
+static void sluggish_get_dlog(char *timestamp)
+{
+ char file_name[MAX_FILENAME_LEN];
+ char file_name1[MAX_FILENAME_LEN];
+ int ret;
+
+ /* Dump platform log files to SLUGGISH_PATH */
+ if (!access(PATH_DLOG, F_OK)) {
+ snprintf(file_name, sizeof(file_name), "%s/%s/dlog_main", SLUGGISH_PATH, timestamp);
+ ret = copy_file(file_name, PATH_DLOG);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", PATH_DLOG, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", PATH_DLOG, file_name);
+ }
+ if (!access(PATH_DLOG1, F_OK)) {
+ snprintf(file_name1, sizeof(file_name1), "%s/%s/dlog_main.1", SLUGGISH_PATH, timestamp);
+ ret = copy_file(file_name1, PATH_DLOG1);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", PATH_DLOG1, file_name1);
+ else
+ _I("Copy: %s to %s SUCCESS", PATH_DLOG1, file_name1);
+ }
+}
+
+static int sluggish_get_battery_status(void)
+{
+ int capacity, ret;
+ DBusMessage *msg;
+
+ /* Get battery status from deviced */
+ msg = dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY,
+ GET_BATTERY_CAPACITY,
+ NULL, NULL);
+ if (!msg) {
+ _E("Failed to sync DBUS message.");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &capacity, DBUS_TYPE_INVALID);
+ dbus_message_unref(msg);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return RESOURCED_ERROR_FAIL;
+ }
+ return capacity;
+}
+
+static void sluggish_get_summary(char *timestamp, int slug_vertical, int pid, double cpu_usage)
+{
+ char file_name[MAX_FILENAME_LEN];
+ char buf[MAX_BUF_LEN] = "";
+ char batbuf[MAX_BUF_LEN] = "";
+ char proc[MAX_FILENAME_LEN];
+ struct statvfs s;
+ double int_tot = 0;
+ double int_free = 0;
+ double ext_tot = 0;
+ double ext_free = 0;
+ FILE *fptr = NULL;
+ FILE *fp = NULL;
+ int batteryp;
+ size_t size;
+
+ /*
+ * Dump
+ * 1. Storage status
+ * 2. Battery status
+ * 3. Sluggishness type
+ * 4. Sluggish PID/Process
+ * 5. CPU Usage
+ */
+
+ /* Get internal memory status */
+ memset(&s, 0x00, sizeof(struct statvfs));
+ if (storage_get_size(INTERNAL, &s) < 0 ) {
+ _E("Fail to get internal memory size");
+ } else {
+ int_tot = ((double)s.f_frsize * s.f_blocks) / KILOBYTE;
+ int_free = ((double)s.f_bsize * s.f_bavail) / KILOBYTE;
+ _I("Internal Memory Status:Total : %lfKB, Avail : %lfKB", int_tot, int_free);
+ }
+
+ /* Get external memory status */
+ memset(&s, 0x00, sizeof(struct statvfs));
+ if (storage_get_size(EXTERNAL, &s) < 0 ) {
+ _E("Fail to get external memory size");
+ } else {
+ ext_tot = ((double)s.f_frsize * s.f_blocks) / KILOBYTE;
+ ext_free = ((double)s.f_bsize * s.f_bavail) / KILOBYTE;
+ _I("External Memory Status:Total : %lfKB, Avail : %lfKB", ext_tot, ext_free);
+ }
+ snprintf(file_name, sizeof(file_name), "%s/%s/summary", SLUGGISH_PATH, timestamp);
+ if (ext_tot > 0) {
+ snprintf(buf, sizeof(buf), "Internal Memory Status:\nTotal : %lfKB, Avail : %lfKB\nExternal Memory Status:\nTotal : %lfKB, Avail : %lfKB\n",
+ int_tot, int_free, ext_tot, ext_free);
+ } else
+ snprintf(buf, sizeof(buf), "Internal Memory Status:\nTotal : %lfKB, Avail : %lfKB\n", int_tot, int_free);
+
+
+ /* Get Battery status */
+ batteryp = sluggish_get_battery_status();
+ if (batteryp >= 0)
+ snprintf(batbuf, sizeof(batbuf), "Battery percentage:%d%%\n", batteryp);
+
+ /* Open file in SLUGGISH_PATH to dump summary */
+ fptr = fopen(file_name, "w+");
+ if (fptr == NULL) {
+ _E("Failed to open file %s", file_name);
+ return;
+ }
+
+ /* 1. Write storage status */
+ fputs(buf, fptr);
+
+ /* 2. Write battery status */
+ if (batteryp >= 0)
+ fputs(batbuf, fptr);
+
+ /* 3. Write sluggishness type */
+ if (slug_vertical == GRAPHICS)
+ fputs("Sluggishness type: GRAPHICS\n", fptr);
+ else if (slug_vertical == MULTIMEDIA)
+ fputs("Sluggishness type: MULTIMEDIA\n", fptr);
+
+ /* 4. Write sluggish PID and process name */
+ fprintf(fptr,"Sluggish PID:%d\n", pid);
+
+ /* Get process name */
+ snprintf(proc, sizeof(proc), "/proc/%d/cmdline", pid);
+ fp = fopen(proc, "rb");
+ if (fp) {
+ memset(buf, 0x00, sizeof(buf));
+ size = fread(buf, 1, MAX_BUF_LEN, fp);
+ if (size > 0) {
+ fputs("Sluggish Process: ", fptr);
+ fputs(buf, fptr);
+ fputs("\n", fptr);
+ }
+ fclose(fp);
+ }
+
+ /* 5. Write CPU usage */
+ fprintf(fptr,"CPU used:%3.2lf%%, idle:%3.2lf%%\n", cpu_usage, (100 - cpu_usage));
+ fclose(fptr);
+}
+
+static void sluggish_get_sys_status(char *timestamp, int slug_vertical, int pid)
+{
+ char dir_name[MAX_FILENAME_LEN];
+ double cu;
+ struct cpu_stat cs1;
+ struct cpu_stat cs2;
+
+ /* Get first cpu stat reading */
+ memset(&cs1, 0x00, sizeof(struct cpu_stat));
+ memset(&cs2, 0x00, sizeof(struct cpu_stat));
+
+ proc_cpu_stat(&cs1);
+
+ /* Get current timestamp */
+ time_stamp(timestamp);
+
+ /*
+ * Create dir to store the system snapshot
+ * All the data captured on sluggish detection notification will be
+ * stored in this directory for uploading later
+ */
+ /* Note : Data is captured only on receiving "SluggishDetected" signal */
+ snprintf(dir_name, sizeof(dir_name), "%s/%s", SLUGGISH_PATH, timestamp);
+ if (mkdir(dir_name, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
+ if (errno != EEXIST) {
+ _E("Failed to create dir %s", dir_name);
+ return;
+ }
+ }
+ _I("Created %s successfully", dir_name);
+
+ /* Get process Status */
+ sluggish_get_psinfo(timestamp);
+
+ /* Get Memory Status */
+ sluggish_get_mem_status(timestamp);
+
+ /* Get dlog */
+ sluggish_get_dlog(timestamp);
+
+ /* Get second cpu stat reading */
+ proc_cpu_stat(&cs2);
+
+ /* Get CPU usage % */
+ cu = proc_cpu_usage(&cs1, &cs2);
+
+ /* Get storage, battaery, cpu status */
+ sluggish_get_summary(timestamp, slug_vertical, pid, cu);
+
+ /* If current CPU utilization is > threshold (CPU_THRESHOLD) delay popup display */
+ if ((unsigned int)cu > CPU_THRESHOLD) {
+ _I("CPU used(%d%%) > %d%% - Delaying popup display ", (unsigned int)cu, CPU_THRESHOLD);
+ g_timeout_add_seconds(POPUP_DELAY_TIME, sluggish_popup_cb, (void *)slug_vertical);
+ return;
+ }
+ sluggish_launch_popup(slug_vertical);
+}
+
+static void sluggish_graphics(pid_t pid)
+{
+ char timestamp[MAX_TIMESTAMP_LEN];
+ struct timeval timeval;
+ struct timeval interval;
+ static pid_t sluggish_graphics_last_pid; /* PID of last graphics sluggishness */
+ static struct timeval sluggish_graphics_last_ts; /* Timestamp of last graphics sluggishness */
+
+ /* Process graphics sluggishness */
+ _I("Graphics sluggishness for PID (%d), Count:%d", pid, ++sluggish_count);
+ if (gettimeofday(&timeval, NULL)) {
+ _E("gettimeofday() failed");
+ return;
+ }
+ if (pid == sluggish_graphics_last_pid) {
+ time_diff(&interval, &sluggish_graphics_last_ts, &timeval);
+ _D("Current ts-sec:%ld usec:%ld", timeval.tv_sec, timeval.tv_usec);
+ _D("Last ts-sec:%ld usec:%ld", sluggish_graphics_last_ts.tv_sec, sluggish_graphics_last_ts.tv_usec);
+ _D("Diff-%ld sec, %ld usec\n", interval.tv_sec, interval.tv_usec);
+ /*
+ *If Duplicate(Same PID, Same sluggish type)
+ * "SluggisgDetected" notification is received
+ * within SLUGGISH_DUP_NOTI_TIMELIMIT ignore it
+ */
+ if (interval.tv_sec <= SLUGGISH_DUP_NOTI_TIMELIMIT) {
+ _I("Ignoring update for same PID:%d within timelimit(%ds)", pid, SLUGGISH_DUP_NOTI_TIMELIMIT);
+ return;
+ }
+ }
+ sluggish_graphics_last_pid = pid;
+ sluggish_graphics_last_ts.tv_sec = timeval.tv_sec;
+ sluggish_graphics_last_ts.tv_usec = timeval.tv_usec;
+
+ /* Get system snapshot and dump to SLUGGISH_PATH */
+ sluggish_get_sys_status(timestamp, GRAPHICS, pid);
+}
+
+static void sluggish_multimedia(pid_t pid)
+{
+ char timestamp[MAX_TIMESTAMP_LEN];
+ struct timeval timeval;
+ struct timeval interval;
+ static pid_t sluggish_media_last_pid; /* PID of last multimedia sluggishness */
+ static struct timeval sluggish_media_last_ts; /* Timestamp of last graphics sluggishness */
+
+ /* Process multimedia sluggishness */
+ _I("Multimedia sluggishness for PID (%d), Count:%d", pid, ++sluggish_count);
+ if (gettimeofday(&timeval, NULL)) {
+ _E("gettimeofday() failed");
+ return;
+ }
+ if (pid == sluggish_media_last_pid) {
+ time_diff(&interval, &sluggish_media_last_ts, &timeval);
+ _D("Current ts-sec:%ld usec:%ld", timeval.tv_sec, timeval.tv_usec);
+ _D("Last ts-sec:%ld usec:%ld", sluggish_media_last_ts.tv_sec, sluggish_media_last_ts.tv_usec);
+ _D("Diff-%ld sec, %ld usec\n", interval.tv_sec, interval.tv_usec);
+ /*
+ * If Duplicate(Same PID, Same sluggish type)
+ * "SluggisgDetected" notification is received
+ * within SLUGGISH_DUP_NOTI_TIMELIMIT ignore it
+ */
+ if (interval.tv_sec <= SLUGGISH_DUP_NOTI_TIMELIMIT) {
+ _I("Ignoring update for same PID:%d within timelimit(%ds)", pid, SLUGGISH_DUP_NOTI_TIMELIMIT);
+ return;
+ }
+ }
+ sluggish_media_last_pid = pid;
+ sluggish_media_last_ts.tv_sec = timeval.tv_sec;
+ sluggish_media_last_ts.tv_usec = timeval.tv_usec;
+
+ /* Get system snapshot and dump to SLUGGISH_PATH */
+ sluggish_get_sys_status(timestamp, MULTIMEDIA, pid);
+}
+
+static void sluggish_process(enum sluggish_event event, pid_t pid)
+{
+ switch (event) {
+ case GRAPHICS:
+ sluggish_graphics(pid);
+ break;
+ case MULTIMEDIA:
+ sluggish_multimedia(pid);
+ break;
+ default:
+ break;
+ };
+}
+
+static DBusMessage *edbus_sluggish_detected(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessage *reply;
+ dbus_bool_t ret;
+ DBusError err;
+ pid_t pid = 0;
+ int slug_vertical = 0;
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &slug_vertical,
+ DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID);
+ if (ret == FALSE) {
+ _E("Can't deserialize sluggish detection message ![%s:%s]\n",
+ err.name, err.message);
+ goto error;
+ }
+
+ dbus_error_free(&err);
+ if (slug_vertical != GRAPHICS && slug_vertical != MULTIMEDIA) {
+ _E("Invalid sluggish vertical:%d", slug_vertical);
+ goto error;
+ }
+
+ /* Received "SluggishDetected" message, so get system details and dump to SLUGGISH_PATH */
+ sluggish_process(slug_vertical, pid);
+error:
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+}
+
+static const struct edbus_method edbus_methods[] = {
+ { "SluggishDetected", NULL, "ii", edbus_sluggish_detected },
+ /* Add methods here */
+};
+
+static int sluggish_init(void *data)
+{
+ resourced_ret_c ret;
+ /* Create SLUGGISH_PATH folder to dump system snapshot*/
+ if (mkdir(SLUGGISH_PATH, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
+ if (errno != EEXIST) {
+ _E("Failed to create dir %s", SLUGGISH_PATH);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ _I("Created %s successfully", SLUGGISH_PATH);
+ ret = edbus_add_methods(RESOURCED_PATH_SLUGGISH, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_SLUGGISH);
+ return ret;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops sluggish_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "sluggish",
+ .init = sluggish_init,
+};
+
+MODULE_REGISTER(&sluggish_ops)
--- /dev/null
+/*
+ * 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 swap.c
+ * @desc swap process
+ */
+#include <trace.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <memory-common.h>
+
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "edbus-handler.h"
+#include "swap-common.h"
+#include "config-parser.h"
+#include "lowmem-handler.h"
+#include "notifier.h"
+#include "procfs.h"
+#include "cgroup.h"
+#include "const.h"
+#include "file-helper.h"
+#include "proc-common.h"
+#include "util.h"
+
+#define MEMCG_PATH "/sys/fs/cgroup/memory/"
+#define SWAPCG_RECLAIM "memory.force_reclaim"
+#define MOVE_CHARGE "memory.move_charge_at_immigrate"
+
+#define SWAP_ON_EXEC_PATH "/sbin/swapon"
+#define SWAP_OFF_EXEC_PATH "/sbin/swapoff"
+#define SWAP_MKSWAP_EXEC_PATH "/sbin/mkswap"
+
+#define SWAP_CONF_FILE "/etc/resourced/swap.conf"
+#define SWAP_CONTROL_SECTION "CONTROL"
+#define SWAP_CONF_STREAMS "MAX_COMP_STREAMS"
+#define SWAP_CONF_ALGORITHM "COMP_ALGORITHM"
+#define SWAP_CONF_RATIO "RATIO"
+
+#define SWAP_BACKEND "zram"
+#define SWAP_ZRAM_NUM_DEVICE "1"
+#define SWAP_ZRAM_DEVICE "/dev/zram0"
+#define SWAP_ZRAM_SYSFILE "/sys/block/zram0/"
+#define SWAP_ZRAM_DISK_SIZE SWAP_ZRAM_SYSFILE"disksize"
+#define SWAP_ZRAM_MAX_COMP_STREAMS SWAP_ZRAM_SYSFILE"max_comp_streams"
+#define SWAP_ZRAM_COMP_ALGORITHM SWAP_ZRAM_SYSFILE"comp_algorithm"
+#define SWAP_ZRAM_COMPACT SWAP_ZRAM_SYSFILE"compact"
+#define SWAP_ZRAM_MEM_USED_TOTAL SWAP_ZRAM_SYSFILE"mem_used_total"
+
+#define MBtoB(x) (x<<20)
+#define MBtoPage(x) (x<<8)
+#define BtoMB(x) ((x) >> 20)
+#define BtoPAGE(x) ((x) >> 12)
+
+#define SWAP_PRIORITY 20
+#define SWAP_SORT_MAX 10
+#define MAX_PIDS 3
+#define SWAP_RATIO 0.5
+#define SWAP_FULLNESS_RATIO 0.8
+#define SWAP_FORCE_RECLAIM_NUM_MAX 5
+#define SWAP_RECLIAM_PAGES_MAX 2560
+#define SWAP_RECLIAM_PAGES_MIN 128
+
+enum swap_thread_op {
+ SWAP_OP_ACTIVATE,
+ SWAP_OP_RECLAIM,
+ SWAP_OP_COMPACT,
+ SWAP_OP_END,
+};
+
+struct swap_task {
+ struct proc_app_info *pai;
+ int size;
+};
+
+struct swap_zram_control {
+ int max_comp_streams;
+ char comp_algorithm[5];
+ float ratio;
+ unsigned long swap_size_bytes;
+ unsigned long swap_almost_full_bytes;
+};
+
+struct swap_safe_queue {
+ GQueue *queue;
+ pthread_mutex_t lock;
+};
+
+struct swap_thread_bundle {
+ struct swap_status_msg msg;
+ enum swap_thread_op op;
+};
+
+static struct swap_zram_control swap_control = {
+ .max_comp_streams = -1,
+ .comp_algorithm = "lzo",
+ .ratio = SWAP_RATIO,
+ .swap_size_bytes = 0,
+ .swap_almost_full_bytes = 0,
+};
+
+static pthread_mutex_t swap_mutex;
+static pthread_cond_t swap_cond;
+static struct swap_safe_queue swap_thread_queue;
+static const struct module_ops swap_modules_ops;
+
+static int swap_compact_handler(void *data);
+
+static const char *compact_reason_to_str(enum swap_compact_reason reason)
+{
+ static const char *reasons_table[] = {"lowmem: low", "lowmem: medium",
+ "swap: zram full"};
+ if (reason >= SWAP_COMPACT_LOWMEM_LOW && reason < SWAP_COMPACT_RESASON_MAX)
+ return reasons_table[reason];
+ return "";
+}
+
+enum swap_state swap_get_state(void)
+{
+ struct shared_modules_data *modules_data = get_shared_modules_data();
+
+ ret_value_msg_if(modules_data == NULL, RESOURCED_ERROR_FAIL,
+ "Invalid shared modules data\n");
+
+ return modules_data->swap_data.swap_state;
+}
+
+static void swap_set_state(enum swap_state state)
+{
+ struct shared_modules_data *modules_data = get_shared_modules_data();
+
+ ret_msg_if(modules_data == NULL,
+ "Invalid shared modules data\n");
+
+ if ((state != SWAP_ON) && (state != SWAP_OFF))
+ return;
+
+ modules_data->swap_data.swap_state = state;
+}
+
+
+static pid_t swap_change_state(enum swap_state state)
+{
+ int status;
+ pid_t child_pid;
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ _E("failed to fork");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* child */
+ if (pid == 0) {
+ if (state == SWAP_ON)
+ execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d",
+ SWAP_ZRAM_DEVICE, (char *)NULL);
+ else if (state == SWAP_OFF)
+ execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH,
+ SWAP_ZRAM_DEVICE, (char *)NULL);
+ exit(0);
+ }
+
+ /* parent */
+ child_pid = waitpid(pid, &status, 0);
+ if (child_pid < 0) {
+ _E("can't wait for a pid %d %d %s", pid, status, strerror(errno));
+ return child_pid;
+ }
+
+ swap_set_state(state);
+ return pid;
+}
+
+static int swap_get_disksize_bytes(void)
+{
+ int ret, disksize = 0;
+
+ ret = fread_int(SWAP_ZRAM_DISK_SIZE, &disksize);
+ if (ret == RESOURCED_ERROR_NONE)
+ return disksize;
+
+ return ret;
+}
+
+static inline void swap_add_bundle(struct swap_thread_bundle *bundle)
+{
+ pthread_mutex_lock(&swap_thread_queue.lock);
+ g_queue_push_tail(swap_thread_queue.queue, bundle);
+ pthread_mutex_unlock(&swap_thread_queue.lock);
+}
+
+static int swap_move_to_cgroup_by_pid(enum memcg_type type, pid_t pid)
+{
+ int ret;
+ struct memcg *memcg_swap = NULL;
+ struct memcg_info *mi;
+ struct proc_app_info *pai = find_app_info(pid);
+ GSList *iter_child = NULL;
+
+ ret = lowmem_get_memcg(type, &memcg_swap);
+ if (ret != RESOURCED_ERROR_NONE)
+ return RESOURCED_ERROR_FAIL;
+
+ mi = memcg_swap->info;
+ if (!pai)
+ return place_pid_to_cgroup_by_fullpath(mi->name, pid);
+
+ ret = place_pid_to_cgroup_by_fullpath(mi->name, pai->main_pid);
+ gslist_for_each_item(iter_child, pai->childs) {
+ struct child_pid *child;
+
+ child = (struct child_pid *)(iter_child->data);
+ ret= place_pid_to_cgroup_by_fullpath(mi->name, child->pid);
+ }
+ pai->memory.memcg_idx = MEMCG_SWAP;
+ pai->memory.memcg_info = mi;
+ return ret;
+}
+
+static int swap_move_to_cgroup(struct memcg_info *info, GArray *candidates)
+{
+ int index;
+ struct swap_task tsk;
+ struct proc_app_info *pai = NULL;
+ GSList *iter_child = NULL;
+
+ if (!candidates)
+ return RESOURCED_ERROR_NO_DATA;
+
+ for (index = 0; index < candidates->len; index++) {
+ tsk = g_array_index(candidates, struct swap_task, index);
+ pai = tsk.pai;
+ place_pid_to_cgroup_by_fullpath(info->name, pai->main_pid);
+ gslist_for_each_item(iter_child, pai->childs) {
+ struct child_pid *child;
+
+ child = (struct child_pid *)(iter_child->data);
+ place_pid_to_cgroup_by_fullpath(info->name, child->pid);
+ }
+ pai->memory.memcg_idx = MEMCG_SWAP;
+ pai->memory.memcg_info = info;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_sort_by_oom(const struct swap_task *ta,
+ const struct swap_task *tb)
+{
+ /* sort by oom score adj */
+ assert(ta != NULL);
+ assert(tb != NULL);
+
+ return ((int)(tb->pai->memory.oom_score_adj) -
+ (int)(ta->pai->memory.oom_score_adj));
+}
+
+static int swap_sort_by_vmrss(const struct swap_task *ta,
+ const struct swap_task *tb)
+{
+ /* sort by task memory usage */
+ assert(ta != NULL);
+ assert(tb != NULL);
+
+ return ((int)(tb->size) - (int)(ta->size));
+}
+
+static int swap_prepare_victims(GArray *candidates)
+{
+ GSList *iter = NULL;
+ struct proc_app_info *pai = NULL;
+ struct swap_task victim;
+
+ /*
+ * serch victims from proc_app_list
+ * It was better than searching backround cgroup
+ * because proc_app_list had already known current state and child processes
+ */
+ gslist_for_each_item(iter, proc_app_list) {
+ pai = (struct proc_app_info *)iter->data;
+ if (pai->memory.memcg_idx != MEMCG_BACKGROUND)
+ continue;
+ if (pai->lru_state <= PROC_BACKGROUND)
+ continue;
+
+ memset(&victim, 0, sizeof(struct swap_task));
+ victim.pai = pai;
+ g_array_append_val(candidates, victim);
+ }
+ return candidates->len;
+}
+
+static int swap_reduce_victims(GArray *candidates, int max)
+{
+ int index;
+ struct swap_task tsk;
+ struct proc_app_info *pai = NULL;
+ unsigned int vmrss = 0;
+
+ if (!candidates)
+ return RESOURCED_ERROR_NO_DATA;
+
+ for (index = 0; index < candidates->len; index++) {
+ tsk = g_array_index(candidates, struct swap_task, index);
+ pai = tsk.pai;
+
+ /* Measuring VmRSS is OK as it's anonymous + swapcache */
+ if (proc_get_mem_usage(pai->main_pid, NULL, &vmrss) < 0)
+ continue;
+
+ tsk.size += vmrss;
+
+ if (pai->childs) {
+ GSList *iter_child = NULL;
+
+ gslist_for_each_item(iter_child, pai->childs) {
+ struct child_pid *child;
+
+ child = (struct child_pid *)(iter_child->data);
+ if (proc_get_mem_usage(child->pid, NULL, &vmrss) < 0)
+ continue;
+ tsk.size += vmrss;
+ }
+ }
+ }
+ /* sort by oom_score_adj value, older are better candidates */
+ g_array_sort(candidates, (GCompareFunc)swap_sort_by_oom);
+
+ /* sort by memory usage, swapping bigger will free more memory */
+ g_array_sort(candidates, (GCompareFunc)swap_sort_by_vmrss);
+
+ /* limit the number of potential candidates, after sort by oom */
+ g_array_remove_range(candidates, max, candidates->len - max);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_reclaim_memcg(struct swap_status_msg msg)
+{
+ int ret;
+ int try = SWAP_FORCE_RECLAIM_NUM_MAX;
+ unsigned int usage, usage_after_reclaim, nr_to_reclaim;
+ unsigned long swap_usage;
+
+ /* Test for restarted resourced, where zram already activated */
+ if (swap_control.swap_size_bytes == 0) {
+ swap_control.swap_size_bytes = swap_get_disksize_bytes();
+ swap_control.swap_almost_full_bytes = swap_control.swap_size_bytes * SWAP_FULLNESS_RATIO;
+ swap_change_state(SWAP_ON);
+ }
+ swap_usage = swap_control.swap_size_bytes - KBYTE_TO_BYTE(proc_get_swap_free());
+ if (swap_usage > swap_control.swap_almost_full_bytes) {
+ _D("reclaim omit, almost full swap partition full: %d curr: %d",
+ swap_control.swap_almost_full_bytes, swap_usage);
+ /* Compact swap when we already have full swap partition */
+ swap_compact_handler((void *)SWAP_COMPACT_SWAP_FULL);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ do {
+ ret = memcg_get_usage(msg.info, &usage);
+ if (ret != RESOURCED_ERROR_NONE)
+ usage = 0;
+
+ nr_to_reclaim = BtoPAGE(usage);
+ if (nr_to_reclaim <= SWAP_RECLIAM_PAGES_MIN)
+ break; /* don't reclaim if little gain */
+ if (nr_to_reclaim > SWAP_RECLIAM_PAGES_MAX)
+ nr_to_reclaim = SWAP_RECLIAM_PAGES_MAX;
+
+ ret = cgroup_write_node(msg.info->name, SWAPCG_RECLAIM,
+ nr_to_reclaim);
+ if (ret != RESOURCED_ERROR_NONE)
+ break; /* if we can't reclaim don't continue */
+
+ ret = memcg_get_usage(msg.info, &usage_after_reclaim);
+ if (ret != RESOURCED_ERROR_NONE)
+ usage_after_reclaim = 0;
+
+ if (usage_after_reclaim >= usage)
+ break; /* if we didn't reclaim more, let's stop */
+
+ _D("FORCE_RECLAIM try: %d, before: %d, after: %d",
+ try, usage, usage_after_reclaim);
+ try -= 1;
+ } while ( try > 0 );
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_compact_zram(void)
+{
+ int ret;
+ unsigned int total;
+ static unsigned int last_total;
+
+ ret = fread_uint(SWAP_ZRAM_MEM_USED_TOTAL, &total);
+ if (ret < 0) {
+ _E("fail to read %s", SWAP_ZRAM_MEM_USED_TOTAL);
+ return ret;
+ }
+
+ /*
+ * Until zram size not increased of at least 1 MB from last compaction
+ * then it not makes any sense to compact it again.
+ */
+ if ((total - last_total) < MBtoB(1))
+ return RESOURCED_ERROR_NO_DATA;
+
+ last_total = total;
+ ret = fwrite_int(SWAP_ZRAM_COMPACT, 1);
+ if (ret < 0) {
+ _E("fail to write %s", SWAP_ZRAM_COMPACT);
+ return ret;
+ }
+
+ ret = fread_uint(SWAP_ZRAM_MEM_USED_TOTAL, &total);
+ if (ret < 0) {
+ _E("fail to read %s", SWAP_ZRAM_MEM_USED_TOTAL);
+ return ret;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_move_background_to_swap(struct swap_status_msg *msg)
+{
+ int max_victims, selected;
+ int ret = RESOURCED_ERROR_NONE;
+ GArray *candidates = NULL, *pids_array = NULL;
+ struct memcg *memcg_swap = NULL;
+
+ pids_array = g_array_new(false, false, sizeof(pid_t));
+ if (!pids_array) {
+ _E("failed to allocate memory");
+ ret = RESOURCED_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Get procs to check for swap candidates */
+ memcg_get_pids(msg->info, pids_array);
+ if (pids_array->len == 0) {
+ ret = RESOURCED_ERROR_NO_DATA;
+ goto out;
+ }
+ /*
+ * background cgroup finds victims and moves them to swap group
+ */
+ ret = lowmem_get_memcg(MEMCG_SWAP, &memcg_swap);
+ if (ret != RESOURCED_ERROR_NONE)
+ return RESOURCED_ERROR_FAIL;
+
+ candidates = g_array_new(false, false, sizeof(struct swap_task));
+ if (!candidates) {
+ _E("failed to allocate memory");
+ ret = RESOURCED_ERROR_OUT_OF_MEMORY;
+ goto out;
+ }
+ /*
+ * Let's consider 50% of background apps to be swappable. Using ZRAM
+ * swap makes the operation on swap cheaper. Only anonymous memory
+ * is swaped so the results are limited by size of allocations.
+ */
+ max_victims = pids_array->len >> 1;
+ /* It makes no sense if we will have no candidates */
+ if (max_victims == 0) {
+ ret = RESOURCED_ERROR_NO_DATA;
+ goto out;
+ }
+ if (max_victims > SWAP_SORT_MAX)
+ max_victims = SWAP_SORT_MAX;
+
+ selected = swap_prepare_victims(candidates);
+ if (selected == 0) {
+ ret = RESOURCED_ERROR_NO_DATA;
+ _D("no victims from proc_app_list (pids: %d)", max_victims);
+ goto out;
+ } else if (selected > max_victims)
+ swap_reduce_victims(candidates, max_victims);
+
+ /*
+ * change swap info from background cgroup to swap group
+ * for using same structure to move and swap it
+ */
+ msg->info = memcg_swap->info;
+ msg->type = MEMCG_SWAP;
+ swap_move_to_cgroup(msg->info, candidates);
+out:
+ if (candidates)
+ g_array_free(candidates, TRUE);
+ if (pids_array)
+ g_array_free(pids_array, TRUE);
+ return ret;
+
+}
+
+static int swap_size(void)
+{
+ int size; /* size in bytes */
+ unsigned long ktotalram = lowmem_get_ktotalram(); /* size in kilobytes */
+
+ if (ktotalram >= 900000) /* >= 878 MB */
+ size = 268435456; /* 256 MB */
+ else if (ktotalram < 200000) /* < 195 MB */
+ size = 16777216; /* 16 MB */
+ else
+ size = ktotalram * swap_control.ratio * 1024;
+
+ _D("swapfile size = %d", size);
+
+ return size;
+}
+
+static int swap_mkswap(void)
+{
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ _E("fork for mkswap failed");
+ return pid;
+ } else if (pid == 0) {
+ _D("mkswap starts");
+ execl(SWAP_MKSWAP_EXEC_PATH, SWAP_MKSWAP_EXEC_PATH,
+ SWAP_ZRAM_DEVICE, (char *)NULL);
+ exit(0);
+ } else {
+ wait(0);
+ _D("mkswap ends");
+ }
+
+ return pid;
+}
+
+static int swap_zram_activate(void)
+{
+ int ret;
+ unsigned int swap_size_bytes;
+
+ ret = fwrite_int(SWAP_ZRAM_MAX_COMP_STREAMS, swap_control.max_comp_streams);
+ if (ret < 0) {
+ _E("fail to write max_comp_streams");
+ return ret;
+ }
+
+ ret = fwrite_str(SWAP_ZRAM_COMP_ALGORITHM, swap_control.comp_algorithm);
+ if (ret < 0) {
+ _E("fail to write comp_algrithm");
+ return ret;
+ }
+
+ swap_control.swap_size_bytes = swap_size();
+ swap_control.swap_almost_full_bytes = swap_control.swap_size_bytes * SWAP_FULLNESS_RATIO;
+ ret = fwrite_uint(SWAP_ZRAM_DISK_SIZE, swap_control.swap_size_bytes);
+ if (ret < 0) {
+ _E("fail to write disk_size");
+ return ret;
+ }
+
+ ret = fread_uint(SWAP_ZRAM_DISK_SIZE, &swap_size_bytes);
+ if (ret < 0) {
+ _E("fail to read zram disk_size");
+ return ret;
+ }
+
+ /* Check if zram was sucessfully initialized (zcomp rollback case) */
+ if (swap_size_bytes < swap_control.swap_size_bytes)
+ return RESOURCED_ERROR_OOM;
+
+ ret = swap_mkswap();
+ if (ret < 0) {
+ _E("swap mkswap failed, fork error = %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void swap_activate_in_module(void)
+{
+ int disksize;
+
+ if (swap_get_state() == SWAP_ON)
+ return;
+
+ disksize = swap_get_disksize_bytes();
+ if (disksize <= 0) {
+ if (swap_zram_activate() < 0) {
+ _E("swap cannot be activated");
+ return;
+ }
+ }
+ swap_change_state(SWAP_ON);
+ _D("swap activated");
+}
+
+static void *swap_thread_main(void * data)
+{
+ int is_empty;
+ struct swap_thread_bundle *bundle;
+
+ setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY);
+
+ while (1) {
+ pthread_mutex_lock(&swap_mutex);
+ /* THREAD: WAIT FOR START */
+ pthread_mutex_lock(&swap_thread_queue.lock);
+ is_empty = g_queue_is_empty(swap_thread_queue.queue);
+ pthread_mutex_unlock(&swap_thread_queue.lock);
+ if (is_empty) {
+ /* The queue is empty, wait for thread signal */
+ pthread_cond_wait(&swap_cond, &swap_mutex);
+ }
+
+ /* We're in swap thread, now it's time to dispatch bundles */
+ pthread_mutex_lock(&swap_thread_queue.lock);
+ bundle = g_queue_pop_head(swap_thread_queue.queue);
+ pthread_mutex_unlock(&swap_thread_queue.lock);
+
+ if (!bundle)
+ goto unlock_out;
+
+ switch (bundle->op) {
+ /* Swap activation operttion: mkswap, swapon etc. */
+ case SWAP_OP_ACTIVATE:
+ swap_activate_in_module();
+ break;
+ /* Swap reclaim opertation: move to swap, force_reclaim */
+ case SWAP_OP_RECLAIM:
+ swap_reclaim_memcg(bundle->msg);
+ break;
+ /* Swap compact operation of zsmalloc. */
+ case SWAP_OP_COMPACT:
+ swap_compact_zram();
+ break;
+ case SWAP_OP_END:
+ default:
+ _D("wrong swap thread operation selected");
+ }
+
+ free(bundle);
+unlock_out:
+ pthread_mutex_unlock(&swap_mutex);
+ }
+ return NULL;
+}
+
+static int swap_start_handler(void *data)
+{
+ int ret;
+ struct swap_thread_bundle *bundle;
+
+ if (!data)
+ return RESOURCED_ERROR_NO_DATA;
+
+ bundle = malloc(sizeof(struct swap_thread_bundle));
+ if (!bundle)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ bundle->op = SWAP_OP_RECLAIM;
+ memcpy(&(bundle->msg), data, sizeof(struct swap_status_msg));
+
+ if (bundle->msg.type == MEMCG_BACKGROUND) {
+ ret = swap_move_background_to_swap(&(bundle->msg));
+ /* add bundle only if some processes were moved into swap memcg */
+ if (ret) {
+ free(bundle);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ }
+ swap_add_bundle(bundle);
+
+ /* Try to signal swap thread, that there is some work to do */
+ ret = pthread_mutex_trylock(&swap_mutex);
+ if (ret == 0) {
+ pthread_cond_signal(&swap_cond);
+ pthread_mutex_unlock(&swap_mutex);
+ _I("send signal to swap thread");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (ret && ret == EBUSY) {
+ _D("swap thread already active");
+ } else {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_simple_bundle_sender(enum swap_thread_op operation)
+{
+ int ret;
+ struct swap_thread_bundle *bundle;
+
+ bundle = malloc(sizeof(struct swap_thread_bundle));
+ if (!bundle)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ bundle->op = operation;
+ swap_add_bundle(bundle);
+
+ /* Try to signal swap thread, that there is some work to do */
+ ret = pthread_mutex_trylock(&swap_mutex);
+ if (ret == 0) {
+ pthread_cond_signal(&swap_cond);
+ pthread_mutex_unlock(&swap_mutex);
+ _I("send signal to swap thread");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (ret && ret == EBUSY) {
+ _D("swap thread already active");
+ } else {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_activate_handler(void *data)
+{
+ return swap_simple_bundle_sender(SWAP_OP_ACTIVATE);
+}
+
+static int swap_compact_handler(void *data)
+{
+ _I("compaction request. Reason: %s",
+ compact_reason_to_str((enum swap_compact_reason)data));
+ return swap_simple_bundle_sender(SWAP_OP_COMPACT);
+}
+
+static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ int ret;
+ pid_t pid;
+ struct memcg *memcg_swap;
+ struct swap_status_msg ss_msg;
+
+ ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_START_PID);
+ if (ret == 0) {
+ _D("there is no swap type signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ ret = lowmem_get_memcg(MEMCG_SWAP, &memcg_swap);
+ if (ret != RESOURCED_ERROR_NONE)
+ return;
+ swap_move_to_cgroup_by_pid(MEMCG_SWAP, pid);
+ ss_msg.pid = pid;
+ ss_msg.type = MEMCG_SWAP;
+ ss_msg.info = memcg_swap->info;
+ swap_start_handler(&ss_msg);
+ _I("swap cgroup entered : pid : %d", (int)pid);
+}
+
+static void swap_type_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+ DBusError err;
+ enum swap_state state;
+
+ if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_TYPE) == 0) {
+ _D("there is no swap state signal");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID) == 0) {
+ _D("there is no message");
+ return;
+ }
+
+ if (swap_get_state() != state)
+ swap_change_state(state);
+}
+
+static DBusMessage *edbus_getswaptype(E_DBus_Object *obj, DBusMessage *msg)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ enum swap_state state;
+
+ state = swap_get_state();
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &state);
+
+ return reply;
+}
+
+static struct edbus_method edbus_methods[] = {
+ { "GetSwapType", NULL, "i", edbus_getswaptype },
+ /* Add methods here */
+};
+
+static const struct edbus_signal edbus_signals[] = {
+ /* RESOURCED DBUS */
+ {RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
+ SIGNAL_NAME_SWAP_TYPE, swap_type_edbus_signal_handler, NULL},
+ {RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
+ SIGNAL_NAME_SWAP_START_PID, swap_start_pid_edbus_signal_handler, NULL},
+};
+
+static void swap_dbus_init(void)
+{
+ resourced_ret_c ret;
+
+ edbus_add_signals(edbus_signals, ARRAY_SIZE(edbus_signals));
+
+ ret = edbus_add_methods(RESOURCED_PATH_SWAP, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ ret_msg_if(ret != RESOURCED_ERROR_NONE,
+ "DBus method registration for %s is failed",
+ RESOURCED_PATH_SWAP);
+}
+
+static int load_swap_config(struct parse_result *result, void *user_data)
+{
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, SWAP_CONTROL_SECTION))
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (!strcmp(result->name, SWAP_CONF_STREAMS)) {
+ int value = atoi(result->value);
+ if (value > 0) {
+ swap_control.max_comp_streams = value;
+ _D("max_comp_streams of swap_control is %d",
+ swap_control.max_comp_streams);
+ }
+ } else if (!strcmp(result->name, SWAP_CONF_ALGORITHM)) {
+ if (!strcmp(result->value, "lzo") ||
+ !strcmp(result->value, "lz4")) {
+ strncpy(swap_control.comp_algorithm, result->value,
+ strlen(result->value) + 1);
+ _D("comp_algorithm of swap_control is %s",
+ result->value);
+ }
+ } else if (!strcmp(result->name, SWAP_CONF_RATIO)) {
+ float ratio = atof(result->value);
+ swap_control.ratio = ratio;
+ _D("swap disk size ratio is %.2f", swap_control.ratio);
+ }
+
+ if (swap_control.max_comp_streams < 0) {
+ int cpu = proc_get_cpu_number();
+ if (cpu > 0) {
+ if (cpu > 4)
+ /*
+ * On big.LITLLE we can have 8 cores visible
+ * but there can be used 4. Let's limit it to 4
+ * if there is no specified value in .conf file.
+ */
+ cpu = 4;
+ swap_control.max_comp_streams = cpu;
+ } else
+ swap_control.max_comp_streams = 1;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_thread_create(void)
+{
+ int ret = 0;
+ pthread_t pth;
+
+ pthread_mutex_init(&swap_mutex, NULL);
+ pthread_cond_init(&swap_cond, NULL);
+ pthread_mutex_init(&(swap_thread_queue.lock), NULL);
+ swap_thread_queue.queue = g_queue_new();
+
+ if (!swap_thread_queue.queue) {
+ _E("fail to allocate swap thread queue");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = pthread_create(&pth, NULL, &swap_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for swap_thread failed\n");
+ return ret;
+ } else {
+ pthread_detach(pth);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int swap_init(void)
+{
+ int ret;
+
+ config_parse(SWAP_CONF_FILE, load_swap_config, NULL);
+ ret = swap_thread_create();
+ if (ret) {
+ _E("swap thread create failed");
+ return ret;
+ }
+ swap_dbus_init();
+
+ return ret;
+}
+
+static int swap_check_node(void)
+{
+ FILE *fp;
+
+ fp = fopen(SWAP_ZRAM_DEVICE, "w");
+ if (fp == NULL) {
+ _E("%s open failed", SWAP_ZRAM_DEVICE);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ fclose(fp);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_swap_check_runtime_support(void *data)
+{
+ return swap_check_node();
+}
+
+/*
+ * Quote from: kernel Documentation/cgroups/memory.txt
+ *
+ * Each bit in move_charge_at_immigrate has its own meaning about what type of
+ * charges should be moved. But in any case, it must be noted that an account of
+ * a page or a swap can be moved only when it is charged to the task's current
+ * (old) memory cgroup.
+ *
+ * bit | what type of charges would be moved ?
+ * -----+------------------------------------------------------------------------
+ * 0 | A charge of an anonymous page (or swap of it) used by the target task.
+ * | You must enable Swap Extension (see 2.4) to enable move of swap charges.
+ * -----+------------------------------------------------------------------------
+ * 1 | A charge of file pages (normal file, tmpfs file (e.g. ipc shared memory)
+ * | and swaps of tmpfs file) mmapped by the target task. Unlike the case of
+ * | anonymous pages, file pages (and swaps) in the range mmapped by the task
+ * | will be moved even if the task hasn't done page fault, i.e. they might
+ * | not be the task's "RSS", but other task's "RSS" that maps the same file.
+ * | And mapcount of the page is ignored (the page can be moved even if
+ * | page_mapcount(page) > 1). You must enable Swap Extension (see 2.4) to
+ * | enable move of swap charges.
+ * quote end.
+ *
+ * In our case it's better to set only the bit number 0 to charge only
+ * anon pages. Therefore file pages etc. will be managed directly by
+ * kernel reclaim mechanisms.
+ * That will help focus us only on swapping the memory that we actually
+ * can swap - anonymous pages.
+ * This will prevent from flushing file pages from memory - causing
+ * slowdown when re-launching applications.
+ */
+static void resourced_swap_change_memcg_settings(enum memcg_type type)
+{
+ int ret;
+ struct memcg *memcg_swap = NULL;
+
+ ret = lowmem_get_memcg(type, &memcg_swap);
+ if (ret != RESOURCED_ERROR_NONE)
+ return;
+
+ cgroup_write_node(memcg_swap->info->name, MOVE_CHARGE, 1);
+}
+
+static int resourced_swap_init(void *data)
+{
+ int ret;
+
+ make_cgroup_subdir(MEMCG_PATH, "swap", NULL);
+ resourced_swap_change_memcg_settings(MEMCG_SWAP);
+ resourced_swap_change_memcg_settings(MEMCG_FAVORITE);
+ resourced_swap_change_memcg_settings(MEMCG_PLATFORM);
+ swap_set_state(SWAP_OFF);
+
+ ret = swap_init();
+ if (ret != RESOURCED_ERROR_NONE)
+ return ret;
+
+ register_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start_handler);
+ register_notifier(RESOURCED_NOTIFIER_SWAP_ACTIVATE, swap_activate_handler);
+ register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, swap_activate_handler);
+ register_notifier(RESOURCED_NOTIFIER_SWAP_COMPACT, swap_compact_handler);
+
+ return ret;
+}
+
+static int resourced_swap_finalize(void *data)
+{
+ unregister_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_SWAP_ACTIVATE, swap_activate_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, swap_activate_handler);
+ unregister_notifier(RESOURCED_NOTIFIER_SWAP_COMPACT, swap_compact_handler);
+ g_queue_free(swap_thread_queue.queue);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+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,
+};
+
+MODULE_REGISTER(&swap_modules_ops)
--- /dev/null
+[CONTROL]
+# zram parameter
+COMP_ALGORITHM=lz4
+# swap ratio
+RATIO=0.5
+#SWAP_HARD_LIMIT is the hard limit percent value (should be an integer). Default is 50 (50%).
+SWAP_HARD_LIMIT=50
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(resourced-test C)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+SET(SRCS
+ test.c
+ main.c
+)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED edbus dlog glib-2.0)
+
+FOREACH(flag ${pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+IF("${SLP_TESTS}" STREQUAL "ON")
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer -finstrument-functions")
+ELSE()
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -g -fno-omit-frame-pointer")
+ENDIF()
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE")
+MESSAGE("FLAGS: ${CMAKE_C_FLAGS}")
+SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -pie")
+MESSAGE("FLAGS: ${CMAKE_EXE_LINKER_FLAGS}")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DENABLE_TEST_DLOG")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS})
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
+
+# test smaps
+ADD_EXECUTABLE(test-smaps ${TEST_DIR}/test-smaps.c)
+TARGET_LINK_LIBRARIES(test-smaps resourced_shared)
+INSTALL(TARGETS test-smaps DESTINATION lib/resourced/test)
+
+# test procfs
+ADD_EXECUTABLE(test-procfs ${TEST_DIR}/test-procfs.c)
+TARGET_LINK_LIBRARIES(test-procfs resourced_shared)
+INSTALL(TARGETS test-procfs DESTINATION lib/resourced/test)
+
+# test file-helper
+ADD_EXECUTABLE(test-file-helper ${TEST_DIR}/test-file-helper.c)
+TARGET_LINK_LIBRARIES(test-file-helper
+ resourced_shared ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+INSTALL(TARGETS test-file-helper DESTINATION lib/resourced/test)
+
+# test autolaunch utility
+ADD_EXECUTABLE(sluggish-test ${TEST_DIR}/sluggish-test.c)
+TARGET_LINK_LIBRARIES(sluggish-test resourced_shared)
+INSTALL(TARGETS sluggish-test DESTINATION bin)
+INSTALL(FILES ${TEST_DIR}/sluggish-test.conf DESTINATION /etc/resourced)
+
+ADD_SUBDIRECTORY(activation)
--- /dev/null
+ADD_SUBDIRECTORY(systemd)
+ADD_SUBDIRECTORY(dbus)
--- /dev/null
+SET(SYSTEM_DBUS_SERVICE_DIR "${PREFIX}/share/dbus-1/system-services")
+
+INSTALL(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/org.tizen.system.resourced-test.service
+ DESTINATION
+ ${SYSTEM_DBUS_SERVICE_DIR}
+)
--- /dev/null
+[D-BUS Service]
+Name=org.tizen.system.ResourcedTest
+Exec=/bin/false
+SystemdService=resourced-test.service
+User=root
--- /dev/null
+SET(SD_SYS_UNIT_DIR "${LIBDIR}/systemd/system")
+
+SET(SD_SYS_UNITS
+ resourced-test.service
+)
+
+INSTALL(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/${SD_SYS_UNITS}
+ DESTINATION
+ ${SD_SYS_UNIT_DIR}
+)
--- /dev/null
+[Unit]
+Description=Start the test service
+Requires=dbus.socket
+After=dbus.socket
+
+[Service]
+Type=dbus
+BusName=org.tizen.system.ResourcedTest
+ExecStart=/usr/bin/resourced-test
+KillSignal=SIGUSR1
--- /dev/null
+/*
+ * test
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+
+#include "test.h"
+
+static void test_main(int argc, char **argv)
+{
+ _I("test all");
+ test_init((void *)NULL);
+ test_exit((void *)NULL);
+}
+
+static void unit_test(int argc, char **argv)
+{
+ const struct test_ops *ops;
+
+ ops = test_find(argv[1]);
+ if (!ops) {
+ _E("there is no test ops : %s", argv[1]);
+ return;
+ }
+ ops->unit(argc, argv);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc >= 2)
+ unit_test(argc, argv);
+ else
+ test_main(argc, argv);
+ return 0;
+}
+
--- /dev/null
+
+/*
+ Copyright (c) 2015 - 2016 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.
+
+ 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
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <linux/input.h>
+
+#ifdef RESOURCED_BUILD
+#include "util.h"
+#else
+static inline void closep(int *fd)
+{
+ if (*fd >= 0)
+ close(*fd);
+}
+
+static inline void fclosep(FILE **f)
+{
+ if (*f)
+ fclose(*f);
+}
+
+static inline void pclosep(FILE **f)
+{
+ if (*f)
+ pclose(*f);
+}
+
+#define _cleanup_close_ __attribute__((cleanup(closep)))
+#define _cleanup_fclose_ __attribute__((cleanup(fclosep)))
+#define _cleanup_pclose_ __attribute__((cleanup(pclosep)))
+#endif
+
+#define MAX_ORDER 11
+#define CMD_EXTFRAG "cat /sys/kernel/debug/extfrag/unusable_index"
+#define CMD_KILL_COUNT "grep -i 'we killed' /var/log/resourced.log | wc | awk '{print $1}'"
+#define CMD_VMSTAT "vmstat | tail -n 1"
+
+/* Various Logs file used */
+#define LOG_FILENAME "sluggish-test.log"
+#define LOG_MEMPS "memps.log"
+#define LOG_KILLED_APP "killedapps.log"
+#define LOG_MEMORY "memorystats.log"
+#define LOG_ION "ion_memory.log"
+#define LOG_DMESG "dmesg.log"
+#define MARKER_LINE "#########################################################"
+
+#define APPLIST_FILENAME "/etc/resourced/sluggish-test.conf"
+
+#define KEY_INPUT_DEVICE "/dev/input/event0"
+
+#define SWAP(a,b) do { \
+ int t; \
+ t = a; \
+ a = b; \
+ b = t; \
+ } while (0)
+
+
+#define ADD_EVENT(t, c, v) { \
+ .type = t, \
+ .code = c, \
+ .value = v, \
+ }
+
+#define MAX_APP 128
+
+static int num_app;
+static char applist[MAX_APP+1][128];
+
+struct input_event events[] = {
+ ADD_EVENT(1, 0x00, 0x01),
+ ADD_EVENT(0, 0x00, 0x00),
+ ADD_EVENT(1, 0x00, 0x00),
+ ADD_EVENT(0, 0x00, 0x00),
+};
+
+struct vmstat_field {
+ unsigned long r;
+ unsigned long b;
+ unsigned long swapused;
+ unsigned long memfree;
+ unsigned long membuf;
+ unsigned long memcached;
+ unsigned long si;
+ unsigned long so;
+ unsigned long bi;
+ unsigned long bo;
+ unsigned long in;
+ unsigned long cs;
+ unsigned long us;
+ unsigned long sy;
+ unsigned long id;
+ unsigned long wa;
+ unsigned long st;
+};
+
+static void send_key_event(int keycode)
+{
+ int i;
+ int ret;
+ _cleanup_close_ int fd = -1;
+
+ fd = open(KEY_INPUT_DEVICE, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "%s: Failed to open: %s : \n",
+ KEY_INPUT_DEVICE, strerror(errno));
+ return;
+ }
+ for (i = 0; i < 4; i++)
+ memset(&events[i].time, 0x0, sizeof(struct timeval));
+
+ events[0].code = keycode;
+ events[2].code = keycode;
+
+ for (i = 0; i < 4; i++) {
+ ret = write(fd, &events[i], sizeof(struct input_event));
+ if (ret < 0)
+ return;
+ }
+}
+
+static int get_kill_count(void)
+{
+ FILE *fp = NULL;
+ char output[32];
+ int count = 0;
+
+ fp = popen(CMD_KILL_COUNT, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "[%s]: ERROR: popen failed: %s\n",
+ __func__, strerror(errno));
+ return -1;
+ }
+ if (fgets(output, sizeof(output), fp) != NULL) {
+ count = atoi(output);
+ }
+ pclose(fp);
+ return count;
+}
+
+static float get_average_fraglevel(const char *zone)
+{
+ int i;
+ _cleanup_pclose_ FILE *fp = NULL;
+ float tmp_fraglevel, fraglevel = 0.00F;
+ char discard[256], zonename[8];
+
+ fp = popen(CMD_EXTFRAG, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: %s: Not present!\n", CMD_EXTFRAG);
+ return -1;
+ }
+ while (fscanf(fp, "%*s %*s %*s %s", zonename) == 1) {
+ if (strcmp(zonename, zone) != 0) {
+ char *ret;
+ ret = fgets(discard, sizeof(discard), fp);
+ if (ret == NULL)
+ return 0.00F;
+
+ continue;
+ }
+
+ fraglevel = 0.00F;
+ for (i = 0; i < MAX_ORDER; i++) {
+ if (fscanf(fp, "%f", &tmp_fraglevel) == 1)
+ fraglevel += tmp_fraglevel;
+ else
+ return 0.00F;
+ }
+ }
+
+ /* Convert to summary percent value */
+ fraglevel = (fraglevel * 100) / MAX_ORDER;
+
+ return fraglevel;
+}
+
+static void get_vmstat_data(struct vmstat_field *vmstat)
+{
+ int ret;
+ FILE *fp = NULL;
+ char output[256];
+
+ if (vmstat == NULL) {
+ fprintf(stderr, "[%s]: ERROR: vmstat is NULL! \n", __func__);
+ return;
+ }
+ memset(output, 0, sizeof(output));
+
+ fp = popen(CMD_VMSTAT, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "[%s]: ERROR: popen failed: %s\n",
+ __func__, strerror(errno));
+ return;
+ }
+ while (fgets(output, sizeof(output), fp) != NULL) {
+ ret = sscanf(output, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ &vmstat->r,&vmstat->b,&vmstat->swapused,&vmstat->memfree,&vmstat->membuf,
+ &vmstat->memcached,&vmstat->si,&vmstat->so,&vmstat->bi,&vmstat->bo,&vmstat->in,
+ &vmstat->cs,&vmstat->us,&vmstat->sy,&vmstat->id,&vmstat->wa,&vmstat->st);
+ if (ret < 17) {
+ fprintf(stderr, "[%s]: ERROR: can't get full vmstat "
+ "output! \n", __func__);
+ pclose(fp);
+ return;
+ }
+ }
+ pclose(fp);
+}
+
+
+static void generate_random_array(int *arr)
+{
+ int i,j;
+ int visited[MAX_APP+1] = {0,};
+ static int random = 3;
+
+ srand(random);
+ random = rand() % num_app;
+ j=1;
+ for (i=0; i<num_app; i++) {
+ int a,b;
+ a = arr[i];
+ b = arr[(i + j*random) % num_app];
+ if (visited[a] || visited[b]) continue;
+ SWAP(arr[a], arr[b]);
+ visited[a] = visited[b] = 1;
+ j++;
+ }
+}
+
+static void print_date_in_logs(const char *logname)
+{
+ char cmdline[128] = {0,};
+ char format[64] = {0,};
+ int ret;
+ time_t t;
+ struct tm *tmt;
+
+ t = time(NULL);
+ tmt = localtime(&t);
+ strftime(format, sizeof(format), "%a %d/%b/%Y %T", tmt);
+
+ sprintf(cmdline, "echo \"START TIME: %s\" >> %s", format, logname);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline, "echo \"======================================\" >> %s", logname);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+}
+
+static void dump_logs(void)
+{
+ int ret;
+ char cmdline[256];
+
+ memset(cmdline, 0, sizeof(cmdline));
+
+ print_date_in_logs(LOG_MEMORY);
+ sprintf(cmdline,"free -tm >> %s", LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline,"cat /proc/buddyinfo >> %s", LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline,"cat /proc/meminfo >> %s", LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline,"cat /proc/vmstat >> %s", LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline,"cat /sys/kernel/debug/extfrag/unusable_index >> %s", LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+
+ print_date_in_logs(LOG_MEMPS);
+ sprintf(cmdline,"memps -a >> %s", LOG_MEMPS);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+
+ print_date_in_logs(LOG_ION);
+ sprintf(cmdline,"cat /sys/kernel/debug/ion/heaps/ion_heap_cma_overlay >> %s", LOG_ION);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline,"cat /sys/kernel/debug/ion/heaps/ion_heap_system >> %s", LOG_ION);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+
+ print_date_in_logs(LOG_DMESG);
+ sprintf(cmdline,"dmesg -c >> %s", LOG_DMESG);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+
+ /* Add beginning marker in all log files */
+ sprintf(cmdline, "echo \"%s\" >> %s", MARKER_LINE, LOG_MEMORY);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline, "echo \"%s\" >> %s", MARKER_LINE, LOG_MEMPS);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline, "echo \"%s\" >> %s", MARKER_LINE, LOG_ION);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline, "echo \"%s\" >> %s", MARKER_LINE, LOG_DMESG);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+ sprintf(cmdline, "echo \"%s\" >> %s", MARKER_LINE, LOG_KILLED_APP);
+ ret = system(cmdline);
+ if (ret < 0)
+ return;
+}
+
+static void copy_files_to_storage(void)
+{
+ int ret;
+
+ ret = system("cp -rf /opt/usr/media/Images/* /opt/storage/sdcard/");
+ if (ret < 0)
+ return;
+}
+
+void populate_applist(void)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ char appid[128];
+
+ fp = fopen(APPLIST_FILENAME, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: could not open %s: %s\n",
+ APPLIST_FILENAME, strerror(errno));
+ return;
+ }
+ while (fgets(appid, sizeof(appid), fp) != NULL) {
+ int len = strlen(appid);
+
+ appid[len-1] = '\0';
+ strncpy(applist[num_app++], appid, len);
+ }
+}
+
+int get_launch_status(char *cmdline)
+{
+ FILE *fp;
+ char output[32];
+
+ memset(output, '\0', sizeof(output));
+ fp = popen(cmdline, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Failed to open: %s : %s\n",
+ __func__, cmdline, strerror(errno));
+ return -1;
+ }
+ if (fgets(output, sizeof(output), fp) != NULL) {
+ if (strstr(output, "failed") != NULL) {
+ pclose(fp);
+ return -1;
+ }
+ }
+ pclose(fp);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int i = 0;
+ int count = 1;
+ int ret;
+ time_t t;
+ struct tm *tmt;
+ int index[128] = {0,};
+ int totalkillcount = 0;
+ char *ptr = NULL;
+ int loop_count = 0;
+ _cleanup_fclose_ FILE *fp = NULL;
+
+ if (argc < 2) {
+ fprintf(stderr, "ERROR: Usage: %s <loop count>\n", argv[0]);
+ return -1;
+ }
+ ptr = argv[1];
+ for (ptr = argv[1]; *ptr != '\0'; ptr++) {
+ if (!isdigit(*ptr)) {
+ fprintf(stderr, "ERROR: Usage: <loop count is not integer>\n");
+ return -1;
+ }
+ }
+ loop_count = atoi(argv[1]);
+ num_app = 0;
+
+ /* Populate the applist array, by reading appid from text file */
+ populate_applist();
+ if (num_app == 0) {
+ fprintf(stderr, "ERROR: Failed to populate applist\n");
+ return -1;
+ }
+
+ /* Remove all logs before starting the test */
+ ret = system("/bin/rm -rf *.log");
+ if (ret < 0)
+ return -1;
+ ret = system("/bin/dmesg -C");
+ if (ret < 0)
+ return -1;
+ ret = system("/bin/echo "" > /var/log/resourced.log");
+ if (ret < 0)
+ return -1;
+
+ fp = fopen(LOG_FILENAME, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "%s: Failed to open: %s : %s\n",
+ argv[0], LOG_FILENAME, strerror(errno));
+ return -1;
+ }
+
+ /* Initialize random array */
+ for (i = 0; i < num_app; i++)
+ index[i] = i;
+
+ while (count <= loop_count) {
+ int i = 0;
+ char cmdline[128];
+ int prevkillcount = 0;
+
+ memset(cmdline, 0, sizeof(cmdline));
+
+ ret = system("echo "" > /var/log/resourced.log");
+ if (ret < 0)
+ return -1;
+
+ dump_logs();
+ t = time(NULL);
+ tmt = localtime(&t);
+ strftime(cmdline, sizeof(cmdline), "%a %d/%b/%Y %T", tmt);
+ fprintf(fp, "\nLOOP#%d, START TIME: %s\n", count, cmdline);
+ fprintf(fp, "==============================================\n");
+
+ fflush(fp);
+ fprintf(fp, "Process CPU_Usage(%%) ActualFree(MB) Reclaimable(MB) SwapUsed(MB) Fragmentation(%%) Kill Count\n");
+ fprintf(fp, "--------------------------------------------- ------------ -------------- -------------- ----------- ----------------- ----------\n");
+
+ for (i = 0; i < num_app; i++) {
+ unsigned long actualfree=0;
+ unsigned long reclaimable=0;
+ unsigned long swapused = 0;
+ float fraglevel = 0.00F;
+ float cpu_usage = 0.00F;
+ struct vmstat_field vmstat;
+ int rindex = 0;
+ int killcount = 0;
+
+ memset(cmdline, 0, sizeof(cmdline));
+ memset(&vmstat, 0, sizeof(vmstat));
+ rindex = index[i];
+ sprintf(cmdline,"aul_test launch %s | grep failed", applist[rindex]);
+ ret = system(cmdline);
+ if (ret < 0)
+ return -1;
+ sleep(3);
+ /* Check app launch status for failure */
+ ret = get_launch_status(cmdline);
+ if (ret != 0) {
+ fprintf(stderr, "App Launch Failed: <%s>\n", applist[rindex]);
+ continue;
+ }
+ /* get CPU usage after 3 second of launching */
+ get_vmstat_data(&vmstat);
+ cpu_usage = (vmstat.us + vmstat.sy);
+
+ if (strstr(applist[rindex], "camera") != NULL) {
+ sleep(2);
+ /* Use VOLUME-DOWN key for camera capture */
+ send_key_event(115);
+ sleep(2);
+ }
+ sleep(10);
+ memset(&vmstat, 0, sizeof(vmstat));
+ /* get memory status after 10 seconds of launching */
+ get_vmstat_data(&vmstat);
+ actualfree = vmstat.memfree/1024;
+ reclaimable = (vmstat.membuf + vmstat.memcached)/1024;
+ swapused = vmstat.swapused/1024;
+ /* Check for fragmentation level, only for Normal zone */
+ fraglevel = get_average_fraglevel("Normal");
+ killcount = get_kill_count();
+ if (killcount > prevkillcount)
+ totalkillcount += (killcount - prevkillcount);
+ prevkillcount = killcount;
+ fprintf(fp, "%d) %-34s\t\t%5.2f\t\t%6lu\t\t%8lu\t%4lu\t\t%5.2f\t%20d\n",
+ rindex,applist[rindex],cpu_usage,actualfree,reclaimable,swapused,fraglevel,totalkillcount);
+ sleep(5);
+ /* Wait for 5 more seconds before dumping logs and minimizing */
+ dump_logs();
+ /* Trigger Home key press event, to minimize the app */
+ send_key_event(139);
+ /* Wait for 2 seconds for Home screen to appear */
+ sleep(2);
+ fflush(fp);
+ }
+ /* Suffle elements in the index array, randomly */
+ generate_random_array(index);
+ /* Copy few files to internal/external storage, or SD card */
+ copy_files_to_storage();
+ sprintf(cmdline, "grep -i 'we killed' /var/log/resourced.log >> %s", LOG_KILLED_APP);
+ ret = system(cmdline);
+ if (ret < 0)
+ return -1;
+ count++;
+ sleep(10);
+ }
+ return 0;
+}
+
--- /dev/null
+com.samsung.phone
+com.samsung.message-lite.compose
+com.samsung.contacts
+com.samsung.music-player-lite
+com.samsung.setting
+com.samsung.myfile-lite
+com.facebook.tizen
+com.samsung.memo
+com.samsung.email
+com.samsung.fm-radio-lite
+com.samsung.weather-m
+org.tizen.tizenstore
+com.samsung.themestore
+com.samsung.dropbox
+com.here.tizen.maps
+com.samsung.browser
+com.samsung.calculator
+srfxzv8GKR.YouTube
+com.samsung.mixradio
+com.samsung.video-player-lite
+com.samsung.voicerecorder-lite
+com.samsung.calendar-lite
+com.samsung.clock-lite
+com.samsung.camera-app-lite
+com.samsung.gallery-lite
+com.tencent.wechat.mmapp
+org.tizen.webcontainer
+c4YC9v0L3i.TrueCallerUI
+net.whatsapp.WhatsApp
+kNWnnj0vTH.MyGalaxy
+iIBRPRJerv.Cricbuzz
+Ynaz8RbwZd.NewsHunt
+com.samsung.google-search
+org.tizen.tizenseckeystring
+com.gameloft.tizen.HEP.GloftANTZ
+com.games2win.worldcupcricketchamp
+com.samsung.smart-manager
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "file-helper.h"
+
+#define STR_TEST_FILE "/tmp/test-file-helper-str"
+#define INT_TEST_FILE "/tmp/test-file-helper-int"
+#define UINT_TEST_FILE "/tmp/test-file-helper-uint"
+
+#define TEST_STR "hello"
+#define TEST_INT -2563
+#define TEST_UINT 3253
+
+static void test_fwrite_str(void)
+{
+ int r;
+ char buf[256];
+
+ r = fwrite_str(STR_TEST_FILE, "test");
+ if (r < 0)
+ fprintf(stderr, "Error: failed to test fwrite_str: %s\n", strerror_r(-r, buf, sizeof(buf)));
+}
+
+static void test_fwrite_fread_int(void)
+{
+ int i = 0;
+ int r;
+ char buf[256];
+
+ r = fwrite_int(INT_TEST_FILE, TEST_INT);
+ if (r < 0)
+ fprintf(stderr, "Error: failed to test fwrite_int: %s\n", strerror_r(-r, buf, sizeof(buf)));
+
+ r = fread_int(INT_TEST_FILE, &i);
+ if (r < 0)
+ fprintf(stderr, "Error: failed to test fread_int: %s\n", strerror_r(-r, buf, sizeof(buf)));
+
+ if (i != TEST_INT)
+ fprintf(stderr, "Error: read int(%d) mismatch with (%d)\n", i, TEST_INT);
+}
+
+static void test_fwrite_fread_uint(void)
+{
+ uint32_t u = 0;
+ int r;
+ char buf[256];
+
+ r = fwrite_uint(UINT_TEST_FILE, TEST_UINT);
+ if (r < 0)
+ fprintf(stderr, "Error: failed to test fwrite_uint: %s\n", strerror_r(-r, buf, sizeof(buf)));
+
+ r = fread_uint(UINT_TEST_FILE, &u);
+ if (r < 0)
+ fprintf(stderr, "Error: failed to test fwrite_uint: %s\n", strerror_r(-r, buf, sizeof(buf)));
+
+ if (u != TEST_UINT)
+ fprintf(stderr, "Error: read uint(%u) mismatch with (%u)\n", u, TEST_UINT);
+}
+
+int main(int argc, char *argv[])
+{
+ test_fwrite_str();
+ test_fwrite_fread_int();
+ test_fwrite_fread_uint();
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "util.h"
+#include "procfs.h"
+
+int main(int argc, char *argv[])
+{
+
+ int ret;
+ struct meminfo mitc1, mitc2;
+ unsigned int swap_free;
+ unsigned int mem_available;
+
+ printf("# proc_get_mem_available \n");
+ mem_available = proc_get_mem_available();
+ printf("MemAvailable: %u MB ( %u KB)\n",
+ mem_available, KBYTE_TO_BYTE(mem_available));
+
+ printf("# proc_get_swap_free \n");
+ swap_free = proc_get_swap_free();
+ printf("SwapFree: %u KB ( %u B)\n",
+ swap_free, KBYTE_TO_BYTE(swap_free));
+
+ printf("# proc_get_meminfo (3 bit mask) \n");
+ ret = proc_get_meminfo(&mitc1, MEMINFO_MASK_MEM_TOTAL|MEMINFO_MASK_SWAP_TOTAL|MEMINFO_MASK_VMALLOC_CHUNK);
+ if (ret < 0)
+ printf("Error from proc_get_meminfo error: %d \n", ret);
+
+ printf("MemTotal: %u KB \nSwapTotal: %u KB \nVmallocChunk: %u KB \n",
+ mitc1.value[MEMINFO_ID_MEM_TOTAL],
+ mitc1.value[MEMINFO_ID_SWAP_TOTAL],
+ mitc1.value[MEMINFO_ID_VMALLOC_CHUNK]);
+
+ printf("# proc_get_meminfo (mask all) \n");
+ ret = proc_get_meminfo(&mitc2, MEMINFO_MASK_ALL);
+ if (ret < 0)
+ printf("Error from proc_get_meminfo error: %d \n", ret);
+
+ printf("MemTotal: %u KB \nBuffers: %u KB \nSwapTotal: %u KB \nPageTables: %u KB \nCommitLimit: %u KB \nVmallocChunk: %u KB \n",
+ mitc2.value[MEMINFO_ID_MEM_TOTAL],
+ mitc2.value[MEMINFO_ID_BUFFERS],
+ mitc2.value[MEMINFO_ID_SWAP_TOTAL],
+ mitc2.value[MEMINFO_ID_PAGE_TABLES],
+ mitc2.value[MEMINFO_ID_COMMIT_LIMIT],
+ mitc2.value[MEMINFO_ID_VMALLOC_CHUNK]);
+
+ return 0;
+}
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "smaps.h"
+
+int main(int argc, char *argv[])
+{
+ _cleanup_smaps_free_ struct smaps *maps;
+ int i, j;
+ int r;
+
+ r = smaps_get(atoi(argv[1]), &maps, SMAPS_MASK_ALL);
+ if (r < 0) {
+ fprintf(stderr, "failed\n");
+ goto exit;
+ }
+
+
+ for (i = 0; i < maps->n_map; i++) {
+ fprintf(stdout, "%x-%-15x %-10s %s\n",
+ maps->maps[i]->start,
+ maps->maps[i]->end,
+ maps->maps[i]->mode,
+ maps->maps[i]->name);
+
+ for (j = 0; j < SMAPS_ID_MAX; j++) {
+ fprintf(stdout, "%-23s: %u\n",
+ smap_id_to_string(j),
+ maps->maps[i]->value[j]);
+ }
+ }
+
+exit:
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * test
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+#include <glib.h>
+
+#include "test.h"
+
+static GSList *test_header;
+
+void test_add(const struct test_ops *module)
+{
+ if (module->priority == TEST_PRIORITY_HIGH)
+ test_header = g_slist_prepend(test_header, (gpointer)module);
+ else
+ test_header = g_slist_append(test_header, (gpointer)module);
+}
+
+void test_remove(const struct test_ops *module)
+{
+ test_header = g_slist_remove(test_header, (gpointer)module);
+}
+
+const struct test_ops *test_find(const char *name)
+{
+ GSList *iter;
+ const struct test_ops *module;
+
+ gslist_for_each_item(iter, test_header) {
+ module = (struct test_ops *)iter->data;
+ if (!strcmp(module->name, name))
+ return module;
+ }
+ return NULL;
+}
+
+void test_init(void *data)
+{
+ GSList *iter;
+ const struct test_ops *module;
+
+ gslist_for_each_item(iter, test_header) {
+ module = (struct test_ops *)iter->data;
+ _D("Initialize [%s] module\n", module->name);
+ if (module->init)
+ module->init(data);
+ }
+}
+
+void test_exit(void *data)
+{
+ GSList *iter;
+ const struct test_ops *module;
+
+ gslist_for_each_item(iter, test_header) {
+ module = (struct test_ops *)iter->data;
+ _D("Deinitialize [%s] module\n", module->name);
+ if (module->exit)
+ module->exit(data);
+ }
+}
--- /dev/null
+/*
+ * test
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 __TEST_H__
+#define __TEST_H__
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <E_DBus.h>
+
+#ifdef ENABLE_TEST_DLOG
+#define LOG_TAG "TEST"
+#include <dlog.h>
+#define _D(fmt, arg...) \
+ do { LOGD(fmt, ##arg); } while(0)
+#define _I(fmt, arg...) \
+ do { LOGI(fmt, ##arg); } while(0)
+#define _W(fmt, arg...) \
+ do { LOGW(fmt, ##arg); } while(0)
+#define _E(fmt, arg...) \
+ do { LOGE(fmt, ##arg); } while(0)
+#define _SD(fmt, arg...) \
+ do { SECURE_LOGD(fmt, ##arg); } while(0)
+#define _SI(fmt, arg...) \
+ do { SECURE_LOGI(fmt, ##arg); } while(0)
+#define _SW(fmt, arg...) \
+ do { SECURE_LOGW(fmt, ##arg); } while(0)
+#define _SE(fmt, arg...) \
+ do { SECURE_LOGE(fmt, ##arg); } while(0)
+#else
+#define _D(...) do { } while (0)
+#define _I(...) do { } while (0)
+#define _W(...) do { } while (0)
+#define _E(...) do { } while (0)
+#define _SD(...) do { } while (0)
+#define _SI(...) do { } while (0)
+#define _SW(...) do { } while (0)
+#define _SE(...) do { } while (0)
+#endif
+
+
+#define gslist_for_each_item(item, list) \
+ for(item = list; item != NULL; item = g_slist_next(item))
+
+#define TEST_WAIT_TIME_INTERVAL 2
+
+enum test_priority {
+ TEST_PRIORITY_NORMAL = 0,
+ TEST_PRIORITY_HIGH,
+};
+
+struct test_ops {
+ enum test_priority priority;
+ char *name;
+ void (*init) (void *data);
+ void (*exit) (void *data);
+ int (*start) (void);
+ int (*stop) (void);
+ int (*status) (void);
+ int (*unit) (int argc, char **argv);
+};
+
+enum test_ops_status {
+ TEST_OPS_STATUS_UNINIT,
+ TEST_OPS_STATUS_START,
+ TEST_OPS_STATUS_STOP,
+ TEST_OPS_STATUS_MAX,
+};
+
+void test_init(void *data);
+void test_exit(void *data);
+
+static inline int test_start(const struct test_ops *c)
+{
+ if (c && c->start)
+ return c->start();
+
+ return -EINVAL;
+}
+
+static inline int test_stop(const struct test_ops *c)
+{
+ if (c && c->stop)
+ return c->stop();
+
+ return -EINVAL;
+}
+
+static inline int test_get_status(const struct test_ops *c)
+{
+ if (c && c->status)
+ return c->status();
+
+ return -EINVAL;
+}
+
+#define TEST_OPS_REGISTER(c) \
+static void __CONSTRUCTOR__ module_init(void) \
+{ \
+ test_add(c); \
+} \
+static void __DESTRUCTOR__ module_exit(void) \
+{ \
+ test_remove(c); \
+}
+
+void test_add(const struct test_ops *c);
+void test_remove(const struct test_ops *c);
+const struct test_ops *test_find(const char *name);
+#endif
--- /dev/null
+/*
+ * 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 timer-slack.c
+ * @desc control timer about timer-slack cgroup
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+
+#include "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "edbus-handler.h"
+#include "resourced.h"
+#include "trace.h"
+#include "vconf.h"
+#include "cgroup.h"
+#include "config-parser.h"
+#include "const.h"
+#include "timer-slack.h"
+#include "notifier.h"
+#include "procfs.h"
+#include "proc-common.h"
+
+#include <resourced.h>
+#include <trace.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#define TIMER_EXCLUDE_CGROUP "exclude"
+#define TIMER_SERVICE_CGROUP "service"
+#define TIMER_BACKGRD_CGROUP "background"
+#define TIMER_STATUS_LCDOFF "LCDOFF"
+#define TIMER_SLACK_ROOT NULL
+#define TIMER_STATUS_POWERSAVING "POWERSAVING"
+
+#define TIMER_CONF_FILE "/etc/resourced/timer-slack.conf"
+#define EXCLUDE_CONF_SECTION "EXCLUDE_TIMER_SLACK"
+#define EXCLUDE_CONF_NAME "EXCLUDE_PROC_NAME"
+
+#define TIMER_SLACK_MODE "/timer_slack.timer_mode"
+#define TIMER_SLACK_VALUE "/timer_slack.min_slack_ns"
+
+struct timer_slack_class {
+ char *name;
+ int timer_mode;
+ int slack_value;
+};
+
+enum {
+ TIMER_SLACK_DEFAULT,
+ TIMER_SLACK_SERVICE,
+ TIMER_SLACK_BACKGROUND,
+ TIMER_SLACK_LCDOFF,
+ TIMER_SLACK_POWERSAVIG,
+ TIMER_SLACK_MAX,
+};
+
+static struct timer_slack_class timer_slack[TIMER_SLACK_MAX] = {
+ {"DEFAULT", 0, 0},
+ {TIMER_SERVICE_CGROUP, 0, 0},
+ {TIMER_BACKGRD_CGROUP, 0, 0},
+ {TIMER_STATUS_LCDOFF, 0, 0},
+ {TIMER_STATUS_POWERSAVING, 0, 0},
+};
+
+static int current_root_timer_state = TIMER_SLACK_DEFAULT;
+
+static const struct module_ops timer_modules_ops;
+static const struct module_ops *timer_ops;
+
+static int timer_slack_write(char *sub_cgroup, char *node, int val)
+{
+ char path_buf[MAX_PATH_LENGTH];
+ int ret;
+ if (sub_cgroup) {
+ snprintf(path_buf, sizeof(path_buf), "%s/%s", TIMER_CGROUP_PATH, sub_cgroup);
+ ret = cgroup_write_node(path_buf, node, val);
+ } else
+ ret = cgroup_write_node(TIMER_CGROUP_PATH, node, val);
+ return ret;
+}
+
+static int control_timer_state(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ int ret;
+ ret = timer_slack_write(TIMER_SERVICE_CGROUP, CGROUP_FILE_NAME, p_data->pid);
+ _I("move to service timer slack cgroup : pid (%d), ret (%d)", p_data->pid, ret);
+ return ret;
+}
+
+static int wakeup_timer_state(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ int ret;
+ ret = timer_slack_write(TIMER_EXCLUDE_CGROUP, CGROUP_FILE_NAME, p_data->pid);
+ return ret;
+}
+
+static int background_timer_state(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ int ret;
+ ret = timer_slack_write(TIMER_BACKGRD_CGROUP, CGROUP_FILE_NAME, p_data->pid);
+ return ret;
+}
+
+static int active_timer_state(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ int ret;
+ ret = timer_slack_write(TIMER_EXCLUDE_CGROUP, CGROUP_FILE_NAME, p_data->pid);
+ return ret;
+}
+
+static int inactive_timer_state(void *data)
+{
+ struct proc_status *p_data = (struct proc_status*)data;
+ int ret;
+ ret = timer_slack_write(TIMER_SLACK_ROOT, CGROUP_FILE_NAME, p_data->pid);
+ return ret;
+}
+
+static int timer_lcd_off(void *data)
+{
+ if (current_root_timer_state == TIMER_SLACK_DEFAULT) {
+ timer_slack_write(TIMER_SLACK_ROOT, TIMER_SLACK_MODE,
+ timer_slack[TIMER_SLACK_LCDOFF].timer_mode);
+ timer_slack_write(TIMER_SLACK_ROOT, TIMER_SLACK_VALUE,
+ timer_slack[TIMER_SLACK_LCDOFF].slack_value);
+ }
+ current_root_timer_state = TIMER_SLACK_LCDOFF;
+ return RESOURCED_ERROR_NONE;
+}
+
+static int timer_lcd_on(void *data)
+{
+ if (current_root_timer_state == TIMER_SLACK_LCDOFF) {
+ timer_slack_write(TIMER_SLACK_ROOT, TIMER_SLACK_MODE,
+ timer_slack[TIMER_SLACK_DEFAULT].timer_mode);
+ timer_slack_write(TIMER_SLACK_ROOT, TIMER_SLACK_VALUE,
+ timer_slack[TIMER_SLACK_DEFAULT].slack_value);
+ current_root_timer_state = TIMER_SLACK_DEFAULT;
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void set_default_cgroup_value(void)
+{
+ int i;
+ char *cgroup;
+ for (i = 0; i < TIMER_SLACK_MAX; i++) {
+ if (i == TIMER_SLACK_DEFAULT)
+ cgroup = TIMER_SLACK_ROOT;
+ else if (i == TIMER_SLACK_SERVICE)
+ cgroup = TIMER_SERVICE_CGROUP;
+ else if (i == TIMER_SLACK_BACKGROUND)
+ cgroup = TIMER_BACKGRD_CGROUP;
+ else
+ continue;
+ timer_slack_write(cgroup, TIMER_SLACK_MODE, timer_slack[i].timer_mode);
+ timer_slack_write(cgroup, TIMER_SLACK_VALUE, timer_slack[i].slack_value);
+ }
+}
+
+static int load_timer_config(struct parse_result *result, void *user_data)
+{
+ int i;
+ pid_t pid = 0;
+
+ if (!result)
+ return -EINVAL;
+
+ if (!strcmp(result->section, EXCLUDE_CONF_SECTION)) {
+ if (strcmp(result->name, EXCLUDE_CONF_NAME))
+ return RESOURCED_ERROR_NO_DATA;
+ pid = find_pid_from_cmdline(result->value);
+ if (pid > 0)
+ timer_slack_write(TIMER_EXCLUDE_CGROUP, CGROUP_FILE_NAME, pid);
+ } else {
+ for (i = 0; i < TIMER_SLACK_MAX; i++) {
+ if (strcmp(result->section, timer_slack[i].name))
+ continue;
+ if (!strcmp(result->name, "timer_mode"))
+ timer_slack[i].timer_mode = atoi(result->value);
+ if (!strcmp(result->name, "min_slack_ns"))
+ timer_slack[i].slack_value = atoi(result->value);
+ }
+ }
+ return RESOURCED_ERROR_NONE;
+}
+
+static void timer_slack_cgroup_init(void)
+{
+ make_cgroup_subdir(TIMER_CGROUP_PATH, TIMER_EXCLUDE_CGROUP, NULL);
+ make_cgroup_subdir(TIMER_CGROUP_PATH, TIMER_SERVICE_CGROUP, NULL);
+ make_cgroup_subdir(TIMER_CGROUP_PATH, TIMER_BACKGRD_CGROUP, NULL);
+
+ config_parse(TIMER_CONF_FILE, load_timer_config, NULL);
+ set_default_cgroup_value();
+}
+
+static int resourced_timer_slack_check_runtime_support(void *data)
+{
+ DIR *dir = 0;
+
+ dir = opendir(TIMER_CGROUP_PATH);
+
+ if (dir) {
+ closedir(dir);
+ return RESOURCED_ERROR_NONE;
+ }
+ return RESOURCED_ERROR_NO_DATA;
+}
+
+static int resourced_timer_slack_init(void *data)
+{
+ timer_ops = &timer_modules_ops;
+
+ timer_slack_cgroup_init();
+
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, control_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_RESUME, wakeup_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, wakeup_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, background_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_ACTIVE, active_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_APP_INACTIVE, inactive_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_LCD_ON, timer_lcd_on);
+ register_notifier(RESOURCED_NOTIFIER_LCD_OFF, timer_lcd_off);
+ register_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, wakeup_timer_state);
+ register_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, control_timer_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_timer_slack_finalize(void *data)
+{
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, control_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, wakeup_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, wakeup_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, background_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_ACTIVE, active_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_INACTIVE, inactive_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_LCD_ON, timer_lcd_on);
+ unregister_notifier(RESOURCED_NOTIFIER_LCD_OFF, timer_lcd_off);
+ unregister_notifier(RESOURCED_NOTIFIER_WIDGET_FOREGRD, wakeup_timer_state);
+ unregister_notifier(RESOURCED_NOTIFIER_WIDGET_BACKGRD, control_timer_state);
+ return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops timer_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = TIMER_MODULE_NAME,
+ .init = resourced_timer_slack_init,
+ .exit = resourced_timer_slack_finalize,
+ .check_runtime_support = resourced_timer_slack_check_runtime_support,
+};
+MODULE_REGISTER(&timer_modules_ops)
--- /dev/null
+[EXCLUDE_TIMER_SLACK]
+# predefined excluded process list
+EXCLUDE_PROC_NAME=Xorg
+EXCLUDE_PROC_NAME=enlightenment
+EXCLUDE_PROC_NAME=dbus-daemon
+EXCLUDE_PROC_NAME=amd
+EXCLUDE_PROC_NAME=alarm-server
+EXCLUDE_PROC_NAME=pulseaudio
+EXCLUDE_PROC_NAME=deviced
+EXCLUDE_PROC_NAME=system-syspopup
+EXCLUDE_PROC_NAME=w-home
+
+# set proper value about each timer slack cgroup
+[LCDOFF]
+timer_mode=1
+min_slack_ns=0
+
+[POWERSAVING]
+timer_mode=1
+min_slack_ns=100000000
+
+[service]
+timer_mode=1
+min_slack_ns=0
+
+[background]
+timer_mode=0
+min_slack_ns=500000000
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 timer-slack.h
+ * @desc timer slack common process
+ **/
+
+#ifndef __TIMER_SLACK_COMMON_H__
+#define __TIMER_SLACK_COMMON_H__
+
+#include <sys/types.h>
+#define TIMER_CGROUP_PATH "/sys/fs/cgroup/timer_slack"
+#define TIMER_MODULE_NAME "timer"
+
+enum timer_cgroup_type {
+ TIMER_CGROUP_DEFAULT,
+ TIMER_CGROUP_EXCLUDE,
+ TIMER_CGROUP_SERVICE,
+ TIMER_CGROUP_BACKGRD,
+};
+
+#endif /* __TIMER_SLACK_COMMON_H__ */
--- /dev/null
+#include <config.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "net-cls-cgroup.h"
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC 1000000000L
+#endif
+
+int64_t timespecDiff(struct timespec *time_a, struct timespec *time_b)
+{
+ return (((int64_t)time_a->tv_sec * NSEC_PER_SEC) + time_a->tv_nsec) -
+ (((int64_t)time_b->tv_sec * NSEC_PER_SEC) + time_b->tv_nsec);
+}
+
+#define START_MEASURE {\
+ struct timespec start, end; \
+ clock_gettime(CLOCK_MONOTONIC, &start);
+
+#define END_MEASURE \
+ clock_gettime(CLOCK_MONOTONIC, &end); \
+ int64_t timeElapsed = timespecDiff(&end, &start);\
+ printf("time diff %"PRId64"\n", timeElapsed); \
+ }
+
+#define TEST_NUM 100
+
+int main(void)
+{
+ int i = 0;
+ char cgroup_name[128];
+
+ printf("start measure");
+ for (; i < TEST_NUM; ++i) {
+ snprintf(cgroup_name, sizeof(cgroup_name), "com.samsung.browser%d", i);
+ START_MEASURE
+ make_net_cls_cgroup_with_pid(i, cgroup_name);
+ END_MEASURE
+ }
+
+ return 0;
+}
--- /dev/null
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "app-stat.h"
+#include "macro.h"
+#include "storage.h"
+
+int main(int argc, char **argv)
+{
+ struct application_stat requests[] = {
+ {"emacs", 24, 42},
+ {"vim", 43, 49},
+ {"emacs", 52, 56}
+ };/*It's not standards compliant, but more robust */
+
+ int index;
+ struct application_stat_tree *app_tree = create_app_stat_tree();
+
+ init_database("./base.db");
+
+ for (index = 0; index != ARRAY_SIZE(requests); ++index)
+ g_tree_insert((GTree *) app_tree->tree,
+ (gpointer)index, (gpointer)(requests + index));
+
+ store_result(app_tree, 0); /*0 time period means alway perform storing*/
+ close_database();
+ free_app_stat_tree(app_tree);
+ return 0;
+}
--- /dev/null
+/*
+ * 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 datausage-tool.c
+ * @desc Implement Performance API. Command line utility.
+ *
+ */
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "data_usage.h"
+#include "macro.h"
+#include "resourced.h"
+#include "const.h"
+#include "iface.h"
+#include "config.h"
+#include "trace.h"
+#include "version.h"
+
+enum run_rsml_cmd {
+ UNDEFINED,
+ RESOURCED_APPLY,
+ RESOURCED_GET,
+ RESOURCED_DATA_USAGE,
+ RESOURCED_DATA_USAGE_DETAILS,
+ RESOURCED_EXCLUDE,
+ RESOURCED_REVERT,
+ RESOURCED_GET_RESTRICTIONS,
+ RESOURCED_SET_OPTIONS,
+ RESOURCED_GET_OPTIONS,
+ RESOURCED_SET_QUOTA,
+ RESOURCED_REMOVE_QUOTA,
+ RESOURCED_RESTRICTION_STATE,
+};
+
+struct arg_param {
+ data_usage_selection_rule du_rule;
+ int64_t rcv_limit;
+ 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,
+ resourced_roaming_type *roaming)
+{
+ if (!str)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!strcmp(optarg, "enabled")) {
+ *roaming = RESOURCED_ROAMING_ENABLE;
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (!strcmp(optarg, "disabled")) {
+ *roaming = RESOURCED_ROAMING_DISABLE;
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (!strcmp(optarg, "unknown")) {
+ *roaming = RESOURCED_ROAMING_UNKNOWN;
+ return RESOURCED_ERROR_NONE;
+ }
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+}
+
+static void print_version()
+{
+ printf("Version number: %d.%d-%d\n", MAJOR_VERSION, MINOR_VERSION,
+ PATCH_VERSION);
+}
+
+static void print_usage()
+{
+ puts("run_rsml [Options]");
+ puts(" Application options:");
+ puts(" possible ordering values: ");
+ puts("\t\tappid - order by application id (package name) "
+ "in ascending");
+ puts("\t\tappiddesc - order by application id (package name) "
+ "in descending");
+ puts("\t\tiface - ascending ordering by network interface name");
+ puts("\t\tifacedesc - descending ordering by network interface name");
+ puts("-a [--apply-rst] <package name> - apply restriction");
+ puts("-e [--exclude-rst] <package name> - exclude restriction");
+ puts("-R [--restrictions] <incoming>,<outgoing> "
+ "- restrictions to apply");
+ puts("-r [--revert-rst] <package name> - revert restriction");
+ puts("-l [--list-app-rst] - list of restricted applications");
+ puts("-g [--get] - get counters and restriction for application");
+ puts("-v [--version] - program version");
+ puts("-h [--help] - application help");
+ puts("-u [--data-usage] - data usage");
+ puts("-f [--from] <timestamp> - starting timestamp "
+ "for data usage requests");
+ puts("-t [--to] <timestamp> - ending timestamp "
+ "for data usage requests");
+ puts("-i [--interface] <iface> - interface name");
+ puts("-d [--data-usage-details] [<appid>] - data usage details "
+ "total/for application");
+ puts("-G [--granularity] <seconds> - split data usage results "
+ "into chunks of <seconds>");
+ puts("-O [--options] <set|get> set or get options");
+ puts(" In case of set options:");
+ puts(" -W [--wifi] <1|0> enable or disable wifi");
+ puts(" -D [--datacall] <1|0> enable or disable datacall");
+ puts(" -T [--datausagetimer] <1|0> enable or disable datausage timer");
+ puts(" -L [--datacalllogging] <1|0> enable or disable datacall logging");
+ puts(" -M [--roaming] <enalbled|disabled|unknown> enable or disable "
+ " roaming, unknown by default");
+ 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:I:B";
+
+ const struct option options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"list-app-rst", no_argument, 0, 'l'},
+ {"version", no_argument, 0, 'v'},
+ {"apply-rst", required_argument, 0, 'a'},
+ {"exclude-rst", required_argument, 0, 'e'},
+ {"revert-rst", required_argument, 0, 'r'},
+ {"get", required_argument, 0, 'g'},
+ {"data-usage", no_argument, 0, 'u'},
+ {"from", required_argument, 0, 'f'},
+ {"to", required_argument, 0, 't'},
+ {"interface", required_argument, 0, 'i'},
+ {"data-usage-details", optional_argument, 0, 'd'},
+ {"granularity", required_argument, 0, 'G'},
+ {"restrictions", required_argument, 0, 'R'},
+ {"options", required_argument, 0, 'O'},
+ {"quota", required_argument, 0, 'q'},
+ {"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}
+ };
+
+ int longindex, retval;
+ enum run_rsml_cmd cmd = UNDEFINED;
+ resourced_iface_type iftype;
+
+ while ((retval =
+ getopt_long(argc, argv, optstring, options,
+ &longindex)) != -1) {
+ switch (retval) {
+ case 'h':
+ case '?':
+ print_usage();
+ exit(EXIT_SUCCESS);
+ case 'v':
+ print_version();
+ exit(EXIT_SUCCESS);
+ case 'a':
+ if (!optarg) {
+ printf("apply-rst option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_APPLY;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 'e':
+ if (!optarg) {
+ printf("exclude-rst option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_EXCLUDE;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 'g':
+ cmd = RESOURCED_GET;
+ break;
+ case 'u':
+ cmd = RESOURCED_DATA_USAGE;
+ break;
+ case 'f':
+ if (!optarg) {
+ printf("from option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ if (sscanf(optarg, "%ld", ¶m->du_rule.from) != 1) {
+ printf("Failed to parse 'from' timestamp: %s\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 't':
+ if (!optarg) {
+ printf("to option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ if (sscanf(optarg, "%ld", ¶m->du_rule.to) != 1) {
+ printf("Failed to parse 'to' timestamp: %s\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'i':
+ if (!optarg) {
+ printf("interface option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ iftype = convert_iftype(optarg);
+ if (iftype == RESOURCED_IFACE_UNKNOWN) {
+ printf("Unknown network interface!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* TODO change internal param structure */
+ param->du_rule.iftype = iftype;
+ break;
+ case 'M':
+ if (!optarg) {
+ printf("roaming option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ resourced_ret_c ret_code = convert_roaming(optarg,
+ ¶m->roaming_type);
+
+ if (ret_code != RESOURCED_ERROR_NONE) {
+ printf("Wrong argument of roaming: %s, roaming "
+ "can only be enabled or disabled\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'd':
+ cmd = RESOURCED_DATA_USAGE_DETAILS;
+ if (optarg) {
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ }
+ break;
+ case 'G':
+ if (!optarg) {
+ printf("granularity option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ if (sscanf(optarg, "%d", ¶m->du_rule.granularity) !=
+ 1) {
+ printf("Failed to parse granularity: %s\n",
+ optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'r':
+ if (!optarg) {
+ printf("revert-rst option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_REVERT;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 'l':
+ cmd = RESOURCED_GET_RESTRICTIONS;
+ break;
+ case 'R':
+ if (!optarg) {
+ printf("restrictions option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ if (sscanf
+ (optarg, "%jd,%jd",
+ ¶m->rcv_limit,
+ ¶m->send_limit) != 2) {
+ printf("Failed to parse restrictions\n"
+ "expected 2 integer numbers separated with commas without spaces\n"
+ "got \"%s\"\n", optarg);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'O':
+ if (!optarg) {
+ printf("options option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ if (optarg && strcmp(optarg, "set") == 0)
+ cmd = RESOURCED_SET_OPTIONS;
+ else if (optarg && strcmp(optarg, "get") == 0)
+ cmd = RESOURCED_GET_OPTIONS;
+ break;
+ case 'q':
+ if (!optarg) {
+ printf("Quota option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_SET_QUOTA;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+
+ break;
+ case 'Q':
+ if (!optarg) {
+ printf("Remove quota option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_REMOVE_QUOTA;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 's':
+ if (!optarg) {
+ printf("Restriction state requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ cmd = RESOURCED_RESTRICTION_STATE;
+ free(param->app_id);
+ param->app_id = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 'I':
+ if (!optarg) {
+ printf("Remove quota option requeres an argument.");
+ exit(EXIT_FAILURE);
+ }
+ param->imsi = strndup(optarg, strlen(optarg)+1);
+ break;
+ case 'B':
+ param->ground = RESOURCED_STATE_BACKGROUND;
+ break;
+ default:
+ printf("Unknown option %c\n", (char)retval);
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+ return cmd;
+}
+
+/* common callback for data usage and data usage details
+ * user_data is NULL for data usage
+ * user_data is a non-NULL
+ * (but not necessarily meaningful) for data usage details
+ */
+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("%3d|", info->iftype);
+ else
+ printf("%20s|", info->app_id ? info->app_id : UNKNOWN_APP);
+
+ if (info->interval) {
+ 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("%35s|", "<entire interval>");
+
+ printf("%10lld|%10lld|%3u|%3u|%10s|%20s\n", info->cnt.incoming_bytes,
+ info->cnt.outgoing_bytes,
+ info->roaming, info->hw_net_protocol_type,
+ info->ifname,
+ info->imsi);
+ return RESOURCED_CONTINUE;
+}
+
+static inline int is_valid_range32(const int64_t value)
+{
+ return value >= 0 && value <= 2147483647; /* 2Gb */
+}
+
+/* callback for restriction details
+ */
+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, 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->quota_id);
+ return RESOURCED_CONTINUE;
+}
+
+const char *state_representation[] = {
+ "UNDEFINDED",
+ "ACTIVATED",
+ "REMOVED",
+ "EXCLUDED",
+};
+
+const char *convert_restriction_state(network_restriction_state state) {
+ if (state <= NETWORK_RESTRICTION_UNDEFINDED
+ && state >= NETWORK_RESTRICTION_MAX_VALUE) {
+ fprintf(stderr, "state not in range %d", state);
+ return NULL;
+ }
+
+ return state_representation[state];
+}
+
+void print_restriction_state(resourced_restriction_state state)
+{
+ const char *state_str = convert_restriction_state(state);
+ if (state_str)
+ printf("\nRestriction state: %s\n", state_str);
+}
+
+int main(int argc, char **argv)
+{
+ int ret_code = 0;
+ struct arg_param param;
+ enum run_rsml_cmd cmd = UNDEFINED;
+ if (argc == 1) {
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ memset(¶m, 0, sizeof(struct arg_param));
+ cmd = parse_cmd(argc, argv, ¶m);
+ switch (cmd) {
+ case RESOURCED_APPLY:
+ {
+ int err = 0;
+ resourced_net_restrictions net_rst = {
+ .rs_type = param.ground,
+ .iftype = param.du_rule.iftype,
+ .roaming = param.roaming_type,
+ .imsi = param.imsi,
+ .send_limit = param.send_limit,
+ .rcv_limit = param.rcv_limit,
+ };
+
+ if (!param.du_rule.iftype) {
+ fprintf(stderr, "Apply restriction command requires -i\n");
+ err = RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!is_valid_range32(param.send_limit)) {
+ fprintf(stderr, "Send limit should be in range 0 - 2Gb");
+ err = RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ if (!is_valid_range32(param.rcv_limit)) {
+ fprintf(stderr, "Rcv limit should be in range 0 - 2Gb");
+ err = RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (err)
+ return err;
+
+ ret_code = set_net_restriction(param.app_id,
+ &net_rst);
+ if (ret_code != RESOURCED_ERROR_NONE) {
+ fprintf(stderr, "Failed to set restriction\n");
+ return ret_code;
+ }
+
+ break;
+
+ }
+ case RESOURCED_EXCLUDE:
+ {
+ resourced_net_restrictions rst = {0,};
+ rst.iftype = param.du_rule.iftype;
+ rst.roaming = param.roaming_type;
+
+ ret_code = set_net_exclusion(param.app_id,
+ &rst);
+ if (ret_code != RESOURCED_ERROR_NONE)
+ return ret_code;
+ break;
+ }
+ case RESOURCED_DATA_USAGE:
+ if (param.du_rule.from && param.du_rule.to) {
+ data_usage_foreach(¶m.du_rule, data_usage_callback,
+ NULL);
+ } else {
+ fprintf(stderr, "Data usage commands require both "
+ "--from and --to\n");
+ }
+ break;
+ case RESOURCED_DATA_USAGE_DETAILS:
+ if (param.du_rule.from && param.du_rule.to) {
+ /* see description for data_usage_callback above */
+ data_usage_details_foreach(param.app_id, ¶m.du_rule,
+ data_usage_callback,
+ (void *)1);
+ } else {
+ fprintf(stderr, "Data usage commands require both "
+ "--from and --to\n");
+ }
+ break;
+ case RESOURCED_REVERT:
+ if (param.du_rule.iftype) {
+ const resourced_net_restrictions rst = {
+ .rs_type = param.ground,
+ .iftype = param.du_rule.iftype,
+ .roaming = param.roaming_type,
+ .imsi = param.imsi,
+ };
+
+ ret_code = remove_restriction_full(param.app_id, &rst);
+ }
+ else
+ fprintf(stderr, "Revert restriction commands require -i\n");
+ if (ret_code != RESOURCED_ERROR_NONE)
+ return ret_code;
+ break;
+ case RESOURCED_GET_RESTRICTIONS:
+ printf("Applications are restricted now:\n");
+ ret_code = restrictions_foreach(restriction_callback, NULL);
+ break;
+ case RESOURCED_SET_OPTIONS:
+ {
+ resourced_options options = {0};
+ ret_code = set_resourced_options(&options);
+ break;
+ }
+ case RESOURCED_GET_OPTIONS:
+ {
+ resourced_options options = {0};
+ ret_code = get_resourced_options(&options);
+ break;
+ }
+ case RESOURCED_SET_QUOTA:
+ {
+ data_usage_quota quota = { 0 };
+ time_t quota_start_time = 0;
+ /* TODO in case of refactoring, use internal command line structure instead of public structure for holding param */
+ 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.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");
+ }
+ break;
+ }
+ case RESOURCED_REMOVE_QUOTA:
+ {
+ struct datausage_quota_reset_rule rule = {0};
+
+ 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");
+ }
+ break;
+ }
+ case RESOURCED_RESTRICTION_STATE:
+ {
+ resourced_restriction_state state;
+ if (!param.du_rule.iftype) {
+ fprintf(stderr, "Exclude restriction commands require -i\n");
+ ret_code = RESOURCED_ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ ret_code = get_restriction_state(param.app_id,
+ param.du_rule.iftype, &state);
+
+ print_restriction_state(state);
+ break;
+ }
+ default:
+ ret_code = RESOURCED_ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ return ret_code;
+}
--- /dev/null
+#include <glib.h>
+#include <stdio.h>
+static gboolean int_cmp(const void *ptr1, const void *ptr2)
+{
+ return (GPOINTER_TO_INT(ptr1) == GPOINTER_TO_INT(ptr2)) ? TRUE : FALSE;
+}
+
+int main(void)
+{
+ GHashTable *table = g_hash_table_new(g_direct_hash, int_cmp);
+ void *ptr = 0;
+ g_hash_table_insert(table, GINT_TO_POINTER(42), main);
+ ptr = g_hash_table_lookup(table, GINT_TO_POINTER(42));
+ printf("%p\n%p\n", ptr, main);
+ return 0;
+}
--- /dev/null
+
+
+#include "iface.h"
+
+int main(void)
+{
+ init_iftype();
+ return 0;
+}
--- /dev/null
+#include <inode2pid.h>
+
+int main(int argc, char **argv)
+{
+ int index;
+ for (index = 0; index < 15; ++index)
+ update_inode_pid_map();
+ return 0;
+}
--- /dev/null
+/*
+ * 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 iptables-test.c
+ *
+ */
+
+#include "iptables-rule.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+enum iptables_test_cmd {
+ CMD_INSERT,
+ CMD_APPEND,
+ CMD_DELETE,
+};
+
+int main(int argc, char *argv[])
+{
+ int i;
+ char *opt, *parse;
+ const char *pattern = "c%s_seth_w0"; /* "c4_1_7_seth_w0" */
+ struct ipt_context iptc = {0};
+ struct nfacct_rule rule;
+ enum iptables_test_cmd cmd;
+
+ if (argc <= 2) {
+ puts(" Usage: \n");
+ puts("iptables-test i|a|d counter1 counter2 counter 3 ... ");
+ exit(1);
+ }
+
+ if (strcmp(argv[1], "i") == 0) {
+ cmd = CMD_INSERT;
+ } else if (strcmp(argv[1], "a") == 0) {
+ cmd = CMD_APPEND;
+ } else if (strcmp(argv[1], "d") == 0) {
+ cmd = CMD_DELETE;
+ } else {
+ printf("Unknown command %s", argv[1]);
+ exit(1);
+ }
+
+ memset(&rule, 0, sizeof(struct nfacct_rule));
+ strcpy(rule.ifname, "seth_w0");
+ resourced_ipt_begin(&iptc);
+
+ resourced_ipt_dump(&iptc);
+
+ for (i = 2; i < argc; ++i) {
+ opt = argv[i];
+
+ sprintf(rule.name, pattern, opt);
+ parse = strtok(opt, "_");
+ rule.iotype = atoi(parse);
+ parse = strtok(NULL, "_");
+ rule.iftype = atoi(parse);
+ parse = strtok(NULL, "_");
+ rule.classid = atoi(parse);
+
+ if (cmd == CMD_INSERT)
+ resourced_ipt_prepend(&rule, &iptc);
+ else if (cmd == CMD_APPEND)
+ resourced_ipt_append(&rule, &iptc);
+ else if (cmd == CMD_DELETE)
+ resourced_ipt_remove(&rule, &iptc);
+ }
+
+ return resourced_ipt_commit(&iptc);
+}
--- /dev/null
+#include "net-cls-cgroup.h"
+#include "macro.h"
+
+int main(int argc, char **argv)
+{
+ int_array *pids = get_monitored_pids();
+ array_foreach(key, int, pids) {
+ printf("%d\n", *key);
+ }
+ return 0;
+}
--- /dev/null
+
+#include "inode2pid.h"
+#include "macro.h"
+#include "port2inode.h"
+#include "trace.h"
+
+static void forearch_get_pid(int inode)
+{
+ int pid;
+ _D("Related inode is: %d", inode);
+ pid = get_pid_from_inode(inode);
+ _D("Got pid : %d", pid);
+}
+
+int main(int argc, char **argv)
+{
+ const int port = 1580;
+ GArray *inodes;
+ int index;
+ for (index = 0; index != 15000; ++index)
+ update_port_inode_map();
+
+ inodes = get_inode_from_port(port, GRABBER_PROTO_TCP);
+ update_inode_pid_map();
+ for (index = 0; inodes && index != inodes->len; ++index)
+ forearch_get_pid(g_array_index(inodes, int, index));
+ return 0;
+}
--- /dev/null
+/*
+ * resourced
+ *
+ * Lib for getting process statistics
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <proc_stat.h>
+
+
+
+int main(void)
+{
+ GArray *valid_proc_infos = NULL;
+ GArray *terminated_proc_infos = NULL;
+ proc_stat_system_time st_diff;
+
+ terminated_proc_infos = g_array_new(false, false, sizeof(proc_stat_process_info));
+ valid_proc_infos = g_array_new(false, false, sizeof(proc_stat_process_info));
+
+
+ proc_stat_init();
+
+ while (true) {
+
+ proc_stat_get_process_info(valid_proc_infos, terminated_proc_infos, NULL);
+ proc_stat_get_system_time_diff(&st_diff);
+
+ if (st_diff.total_time != 0) {
+
+ double total_time = st_diff.total_time;
+
+ printf("Total CPU Info : %3.2lf%%us %3.2lf%%sy %3.2lf%%ni %3.2lf%%id %3.2lf%%iowait %3.2lf%%irq %3.2lf%%softirq\n",
+ (double)st_diff.user_time / total_time * 100,
+ (double)st_diff.system_time / total_time * 100,
+ (double)st_diff.nice_time / total_time * 100,
+ (double)st_diff.idle_time / total_time * 100,
+ (double)st_diff.iowait_time / total_time * 100,
+ (double)st_diff.irq_time / total_time * 100,
+ (double)st_diff.softirq_time / total_time * 100);
+
+ unsigned int total, free;
+ if (proc_stat_get_total_mem_size(&total) && proc_stat_get_free_mem_size(&free))
+ printf("Total Memory Info : Total:%dMB Free:%dMB Used:%dMB\n", total, free, total - free);
+
+ unsigned int i = 0;
+ for (i = 0; i < valid_proc_infos->len; ++i) {
+ proc_stat_process_info *ps = &g_array_index(valid_proc_infos, proc_stat_process_info, i);
+
+ if ((ps->active) || (ps->fresh)) {
+ if (ps->fresh)
+ printf("N ");
+ else
+ printf(" ");
+
+ printf("[pid:%d\t name:%40s utime:%3.2lf%% stime:%3.2lf%% rss:%dKb\n",
+ ps->pid, ps->name,
+ (double)(ps->utime_diff)/(double)st_diff.total_time*100,
+ (double)(ps->stime_diff)/(double)st_diff.total_time*100,
+ ps->rss);
+ }
+ }
+
+ for (i = 0; i < terminated_proc_infos->len; ++i) {
+
+ proc_stat_process_info *ps = &g_array_index(terminated_proc_infos, proc_stat_process_info, i);
+
+ printf("T ");
+ printf("[pid:%d\t name:%40s\n",
+ ps->pid, ps->name);
+ }
+
+ }
+
+ usleep(1000000);
+ g_array_set_size(valid_proc_infos, 0);
+ g_array_set_size(terminated_proc_infos, 0);
+
+ printf("-------------------------------------------------------------------------------\n");
+
+ }
+
+ if (valid_proc_infos) {
+ g_array_free(valid_proc_infos, true);
+ valid_proc_infos = NULL;
+ }
+
+
+ if (terminated_proc_infos) {
+ g_array_free(terminated_proc_infos, true);
+ terminated_proc_infos = NULL;
+ }
+
+ proc_stat_finalize();
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * 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 testgrabbing.c
+ * @desc Functionality for testing ktgrabber communications
+ * and related procedures
+ */
+
+#include "trace.h"
+
+#include <data_usage.h>
+#include <mcheck.h>
+#include <Ecore.h>
+
+resourced_cb_ret net_activity_func(struct net_activity_info *info)
+{
+ printf("%20s\t|%8d\t|%8d\t|%8d\t|\n", info->appid, info->iftype,
+ info->type, info->bytes);
+ return RESOURCED_CONTINUE;
+}
+
+int main(int argc, char **argv)
+{
+ resourced_ret_c ret;
+#ifdef NETWORK_DEBUG_ENABLED
+ mtrace();
+ mcheck(0);
+#endif
+ printf("%20s\t|%8s\t|%8s\t|%8s\t|\n", "appid", "iftype",
+ "type", "bytes");
+
+ ret = register_net_activity_cb(net_activity_func);
+ ecore_main_loop_begin();
+ return 0;
+}
--- /dev/null
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "udp-common.h"
+
+int main(void)
+{
+ struct sockaddr_in remote_address;
+
+ int i, slen = sizeof(struct sockaddr_in);
+ char buf[BUF_SIZE];
+
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ prepare_address(AF_INET, PORT, &remote_address);
+
+ if (inet_aton(SRV_IP, &remote_address.sin_addr) == 0) {
+ fprintf(stderr, "inet_aton() failed\n");
+ exit(1);
+ }
+ for (i = 0; i < PACKET_NUMBER; i++) {
+ printf("Sending packet %d\n", i);
+ snprintf(buf, sizeof(buf), "This is packet %d\n", i);
+ if (sendto(s, buf, BUF_SIZE, 0, (struct sockaddr *)&remote_address, slen) == -1) {
+ perror("sendto()");
+ exit(1);
+ }
+ sleep(WAIT_INTERVAL);
+ }
+
+ close(s);
+ return 0;
+}
--- /dev/null
+#include <netinet/in.h>
+#include <string.h>
+
+#include "udp-common.h"
+
+void prepare_address(int family, int port, struct sockaddr_in *addr)
+{
+ memset((char *) addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = family;
+ addr->sin_port = htons(port);
+}
--- /dev/null
+#ifndef _PERF_CONTROL_UTILS_UDP_COMMON_H
+#define _PERF_CONTROL_UTILS_UDP_COMMON_H
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#define BUF_SIZE 512
+#define PACKET_NUMBER 1000
+#define PORT 2012
+#define WAIT_INTERVAL 20
+#define SRV_IP "192.168.129.3"
+
+void prepare_address(int family, int port, struct sockaddr_in *addr);
+
+#endif /*_PERF_CONTROL_UTILS_UDP_COMMON_H*/
+
--- /dev/null
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "udp-common.h"
+
+int main(void)
+{
+ struct sockaddr_in local_address, remote_address;
+ int i;
+ socklen_t slen = sizeof(struct sockaddr_in);
+ char buf[BUF_SIZE];
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (s == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ prepare_address(AF_INET, PORT, &local_address);
+ local_address.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(s, (struct sockaddr *)(&local_address),
+ sizeof(struct sockaddr_in)) == -1) {
+ perror("bind");
+ exit(1);
+ }
+
+ for (i = 0; i < PACKET_NUMBER; i++) {
+ if (recvfrom(s, buf, BUF_SIZE, 0,
+ (struct sockaddr *)&remote_address, &slen) == -1) {
+ perror("recvfrom()");
+ exit(1);
+ }
+ printf("Received packet from %s:%d\nData: %s\n\n",
+ inet_ntoa(remote_address.sin_addr), ntohs(remote_address.sin_port), buf);
+ }
+
+ close(s);
+ return 0;
+}
--- /dev/null
+/*
+ * 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.
+ *
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <Ecore.h>
+
+#include "const.h"
+#include "resourced.h"
+#include "trace.h"
+#include "cgroup.h"
+#include "procfs.h"
+#include "macro.h"
+#include "util.h"
+#include "config-parser.h"
+#include "file-helper.h"
+#include "storage-helper.h"
+#include "systemd-util.h"
+#include "notifier.h"
+#include "module.h"
+#include "module-data.h"
+#include "vip-process.h"
+
+#define VIP_CONF_FILE "/etc/resourced/vip-process.conf"
+
+static char **arg_vip_proc_names = NULL;
+static char **arg_vip_systemd_services = NULL;
+
+static int vip_parse_config_file(void)
+{
+ const ConfigTableItem items[] = {
+ { "VIP_PROCESS", "VIP_PROC_NAME",
+ config_parse_strv, 0, &arg_vip_proc_names },
+ { "VIP_PROCESS", "VIP_SYSTEMD_SERVICE",
+ config_parse_strv, 0, &arg_vip_systemd_services },
+ { NULL, NULL,
+ NULL, 0, NULL }
+ };
+
+ return config_parse_new(VIP_CONF_FILE, (void*) items);
+}
+
+static int vip_create_sub_cgroup(const char *name, pid_t pid)
+{
+ _cleanup_free_ char *cgroup_name = NULL;
+ bool already;
+ int r;
+ char buf[256];
+
+ assert(name);
+ assert(pid);
+
+ r = make_cgroup_subdir(VIP_CGROUP, name, &already);
+ if (r < 0) {
+ _E("failed to create vip sub dir");
+ return r;
+ }
+
+ if (already) {
+ _D("PID(%d) is already registered as VIP sub cgroup(%s)",
+ pid, name);
+ return 0;
+ }
+
+ r = asprintf(&cgroup_name, "%s/%s", VIP_CGROUP, name);
+ if (r < 0) {
+ _E("failed to allocate memory");
+ return -ENOMEM;
+ }
+
+ r = cgroup_write_node(cgroup_name, TASK_FILE_NAME, pid);
+ if (r < 0) {
+ _E("failed to write pid '%d' to '%s': %s",
+ pid, cgroup_name, strerror_r(-r, buf, sizeof(buf)));
+ return r;
+ }
+
+ _D("PID(%d) is registered as VIP sub cgroup(%s)", pid, name);
+
+ return 0;
+}
+
+static void vip_create_proc_name_groups(void)
+{
+ char **pname = NULL;
+ int r;
+
+ if (!arg_vip_proc_names)
+ return;
+
+ FOREACH_STRV(pname, arg_vip_proc_names) {
+ pid_t pid = 0;
+
+ pid = find_pid_from_cmdline(*pname);
+ if (pid > 0) {
+ r = vip_create_sub_cgroup(*pname, pid);
+ if (r < 0)
+ _E("failed to create "
+ "sub cgroup of '%s', ignoring", *pname);
+ } else
+ _D("failed to find pid of name: %s", *pname);
+ }
+}
+
+static void vip_create_systemd_service_groups(void)
+{
+ char **pname = NULL;
+
+ if (!arg_vip_systemd_services)
+ return;
+
+ FOREACH_STRV(pname, arg_vip_systemd_services) {
+ _cleanup_free_ char *err_msg = NULL;
+ unsigned int u = 0;
+ int r;
+
+ r = systemd_get_service_property_as_uint32(*pname,
+ "ExecMainPID",
+ &u,
+ &err_msg);
+ if (r < 0 || !u) {
+ _E("failed to get ExecMainPid of "
+ "'%s' systemd service: %s", *pname, err_msg);
+ continue;
+ }
+
+ if (u > 0) {
+ r = vip_create_sub_cgroup(*pname, (pid_t)u);
+ if (r < 0)
+ _E("failed to create "
+ "sub cgroup of '%s', ignoring", *pname);
+ }
+ }
+}
+
+static int vip_booting_done(void *data)
+{
+ vip_create_proc_name_groups();
+ vip_create_systemd_service_groups();
+
+ return 0;
+}
+
+static int resourced_vip_process_init(void *data)
+{
+ _cleanup_close_ int checkfd = -1;
+ int r;
+ char buf[256];
+
+ r = access(CHECK_RELEASE_PROGRESS, F_OK);
+ if (r == 0) {
+ r = unlink(CHECK_RELEASE_PROGRESS);
+ if (r < 0)
+ _E("failed to remove %s: %m", CHECK_RELEASE_PROGRESS);
+ }
+
+ r = vip_parse_config_file();
+ if (r < 0) {
+ _E("failed to parse vip config file: %s", strerror_r(-r, buf, sizeof(buf)));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!arg_vip_proc_names)
+ return RESOURCED_ERROR_NONE;
+
+ if (!is_mounted(VIP_CGROUP)) {
+ r = make_cgroup_subdir(DEFAULT_CGROUP, "vip", NULL);
+ if (r < 0) {
+ _E("failed to make vip cgroup");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ r = mount_cgroup_subsystem("vip_cgroup", VIP_CGROUP,
+ "none,name=vip_cgroup");
+ if (r < 0) {
+ _E("failed to mount vip cgroup: %m");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ r = set_release_agent("vip", "/usr/bin/vip-release-agent");
+ if (r < 0) {
+ _E("failed to set vip release agent: %s", strerror_r(-r, buf, sizeof(buf)));
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+
+ vip_create_proc_name_groups();
+ vip_create_systemd_service_groups();
+
+ r = register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE,
+ vip_booting_done);
+ if (r < 0) {
+ _E("failed to register notifier BootingDone");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_vip_process_finalize(void *data)
+{
+ strv_free_full(arg_vip_proc_names);
+ strv_free_full(arg_vip_systemd_services);
+
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, vip_booting_done);
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops vip_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "vip-process",
+ .init = resourced_vip_process_init,
+ .exit = resourced_vip_process_finalize,
+};
+
+MODULE_REGISTER(&vip_modules_ops)
--- /dev/null
+[VIP_PROCESS]
+# VIP process list
+VIP_PROC_NAME=Xorg
+VIP_PROC_NAME=enlightenment
+VIP_PROC_NAME=amd
+VIP_SYSTEMD_SERVICE=dbus.service
--- /dev/null
+/*
+ * resourced
+ *
+ * Copyright (c) 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 proc-monitor.h
+ * @desc proc monitor
+ **/
+
+#ifndef __RESOURCED_VIP_PROCESS_H__
+#define __RESOURCED_VIP_PROCESS_H__
+
+#define CHECK_RELEASE_PROGRESS "/tmp/.release_ongoing"
+#define VIP_CGROUP "/sys/fs/cgroup/vip"
+
+#endif /* __RESOURCED_VIP_PROCESS_H__ */
+
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include "trace.h"
+#include "vip-process.h"
+
+#define TIZEN_DEBUG_MODE_FILE "/opt/etc/.debugmode"
+#define DUMP_PATH "/usr/bin/all_log_dump.sh"
+#define REBOOT_PATH "/usr/sbin/reboot"
+#define WAIT_TIMEOUT 10
+#define MAX_TIMES 30
+
+static int check_debugenable(void)
+{
+ if (access(TIZEN_DEBUG_MODE_FILE, F_OK) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static int run_exec(char **argv)
+{
+ int status = 0, times = 0, wpid;
+ pid_t pid = 0;
+ char buf[256];
+
+ if (argv == NULL)
+ return -3;
+
+ pid = fork();
+
+ if (pid == -1)
+ return -1;
+
+ if (pid == 0) {
+ setpgid(0, 0);
+ if (execv(argv[0], argv) == -1) {
+ _E("Error execv: %s.\n", strerror_r(errno, buf, sizeof(buf)));
+ _exit(-1);
+ }
+ _exit(1);
+ }
+ do {
+ wpid = waitpid(pid, &status, WNOHANG);
+ if (wpid == 0) {
+ if (times < MAX_TIMES) {
+ sleep(WAIT_TIMEOUT);
+ times ++;
+ } else {
+ _D("timeout!!, kill child process(%s)\n",
+ argv[0]);
+ kill(pid, SIGKILL);
+ }
+ }
+ } while (wpid == 0 && times <= MAX_TIMES);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int checkfd;
+ char *dumpargv[3] = {DUMP_PATH, "urgent", NULL};
+ char *rebootargv[4] = {REBOOT_PATH, "silent", NULL, NULL};
+ DIR *dir = 0;
+
+ dir = opendir(VIP_CGROUP);
+ if (!dir) {
+ _E("doesn't support cgroup release agent");
+ return 0;
+ }
+ closedir(dir);
+
+ _E("call release agent : [%d:%s]\n", argc, argv[1]);
+
+ /* check previous process */
+ if (access(CHECK_RELEASE_PROGRESS, F_OK) == 0)
+ return 0;
+
+ /* make tmp file */
+ checkfd = creat(CHECK_RELEASE_PROGRESS, 0640);
+ if (checkfd < 0) {
+ _E("fail to make %s file\n", CHECK_RELEASE_PROGRESS);
+ checkfd = 0;
+ }
+
+ /* unmount cgroup for preventing launching another release-agent */
+ _E("systemd service stop");
+ umount2("/sys/fs/cgroup", MNT_FORCE |MNT_DETACH);
+
+ /* check debug level */
+ if (check_debugenable())
+ run_exec(dumpargv);
+
+ /* clear tmp file */
+ if (checkfd) {
+ if (unlink(CHECK_RELEASE_PROGRESS) < 0)
+ _E("fail to remove %s file\n", CHECK_RELEASE_PROGRESS);
+ close(checkfd);
+ }
+
+ sync();
+
+ run_exec(rebootargv);
+ return 0;
+}
+