tizen 2.3.1 release tizen_2.3.1 submit/tizen_2.3.1/20150915.091410 tizen_2.3.1_release
authorjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 16:45:03 +0000 (01:45 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 16:45:03 +0000 (01:45 +0900)
182 files changed:
CMakeLists.txt [new file with mode: 0644]
CMakeLists/resourced.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
data/resourced_proc_exclude.ini [new file with mode: 0644]
data/traffic_db.sql [new file with mode: 0644]
docs/description.txt [new file with mode: 0644]
docs/func.txt [new file with mode: 0644]
docs/traffic_db.txt [new file with mode: 0644]
include/data_usage.h [new file with mode: 0755]
include/powertop-dapi.h [new file with mode: 0644]
include/proc_stat.h [new file with mode: 0644]
include/rd-network.h [new file with mode: 0755]
include/resourced.h [new file with mode: 0644]
libresourced.manifest [new file with mode: 0644]
libresourced.pc.in [new file with mode: 0644]
packaging/add_test_net_activity.patch [new file with mode: 0644]
packaging/resourced-cpucgroup.service [new file with mode: 0644]
packaging/resourced-logging.service [new file with mode: 0644]
packaging/resourced-zram.service [new file with mode: 0644]
packaging/resourced.service [new file with mode: 0644]
packaging/resourced_swapoff.service [new file with mode: 0644]
packaging/system-resource.spec [new file with mode: 0644]
resourced.conf [new file with mode: 0644]
resourced.manifest [new file with mode: 0644]
resourced.rule [new file with mode: 0644]
resourced_nodb.manifest [new file with mode: 0644]
scripts/resourced-cpucgroup.sh [new file with mode: 0644]
scripts/resourced-zram.sh [new file with mode: 0644]
src/common/app-stat.h [new file with mode: 0644]
src/common/appid-helper.c [new file with mode: 0644]
src/common/appid-helper.h [new file with mode: 0644]
src/common/cgroup.c [new file with mode: 0644]
src/common/cgroup.h [new file with mode: 0644]
src/common/config-parser.c [new file with mode: 0644]
src/common/config-parser.h [new file with mode: 0644]
src/common/config.h.in [new file with mode: 0644]
src/common/const.h [new file with mode: 0644]
src/common/daemon-options.h [new file with mode: 0644]
src/common/edbus-handler.c [new file with mode: 0644]
src/common/edbus-handler.h [new file with mode: 0644]
src/common/file-helper.c [new file with mode: 0644]
src/common/file-helper.h [new file with mode: 0644]
src/common/genl.h [new file with mode: 0644]
src/common/logging-common.h [new file with mode: 0644]
src/common/lowmem-common.h [new file with mode: 0644]
src/common/macro.h [new file with mode: 0644]
src/common/module-data.c [new file with mode: 0644]
src/common/module-data.h [new file with mode: 0644]
src/common/module.c [new file with mode: 0644]
src/common/module.h [new file with mode: 0644]
src/common/notifier.c [new file with mode: 0644]
src/common/notifier.h [new file with mode: 0644]
src/common/swap-common.h [new file with mode: 0644]
src/common/trace.h [new file with mode: 0644]
src/common/transmission.h [new file with mode: 0644]
src/cpu/cpu.c [new file with mode: 0644]
src/cpu/cpu.conf [new file with mode: 0644]
src/cpu/logging-cpu.c [new file with mode: 0644]
src/logging/include/logging.h [new file with mode: 0644]
src/logging/logging.c [new file with mode: 0644]
src/memory/logging-memory.c [new file with mode: 0644]
src/memory/lowmem-dbus.c [new file with mode: 0644]
src/memory/lowmem-handler.c [new file with mode: 0644]
src/memory/lowmem-handler.h [new file with mode: 0644]
src/memory/memory_eng.conf [new file with mode: 0644]
src/memory/memory_user.conf [new file with mode: 0644]
src/memory/vmpressure-lowmem-handler.c [new file with mode: 0644]
src/memps/CMakeLists.txt [new file with mode: 0644]
src/memps/memps.c [new file with mode: 0644]
src/network/CMakeLists.txt [new file with mode: 0755]
src/network/app-stat.c [new file with mode: 0644]
src/network/counter-process.c [new file with mode: 0644]
src/network/counter.c [new file with mode: 0644]
src/network/daemon-options.c [new file with mode: 0644]
src/network/datausage-common.c [new file with mode: 0644]
src/network/datausage-quota-processing.c [new file with mode: 0644]
src/network/datausage-quota.c [new file with mode: 0644]
src/network/datausage-vconf-callbacks.c [new file with mode: 0644]
src/network/datausage-vconf-common.c [new file with mode: 0644]
src/network/dummy-tethering.c [new file with mode: 0644]
src/network/dummy_roaming.c [new file with mode: 0644]
src/network/foreach.c [new file with mode: 0644]
src/network/generic-netlink.c [new file with mode: 0644]
src/network/iface-cb.c [new file with mode: 0644]
src/network/iface.c [new file with mode: 0644]
src/network/include/counter-process.h [new file with mode: 0644]
src/network/include/counter.h [new file with mode: 0644]
src/network/include/database.h [new file with mode: 0644]
src/network/include/datausage-common.h [new file with mode: 0644]
src/network/include/datausage-foreach.h [new file with mode: 0644]
src/network/include/datausage-quota-processing.h [new file with mode: 0644]
src/network/include/datausage-quota.h [new file with mode: 0644]
src/network/include/datausage-reset.h [new file with mode: 0644]
src/network/include/datausage-restriction.h [new file with mode: 0644]
src/network/include/datausage-vconf-callbacks.h [new file with mode: 0644]
src/network/include/datausage-vconf-common.h [new file with mode: 0644]
src/network/include/errors.h [new file with mode: 0644]
src/network/include/generic-netlink.h [new file with mode: 0644]
src/network/include/iface-cb.h [new file with mode: 0644]
src/network/include/iface.h [new file with mode: 0644]
src/network/include/net-cls-cgroup.h [new file with mode: 0644]
src/network/include/netlink-restriction.h [new file with mode: 0644]
src/network/include/nfacct-rule.h [new file with mode: 0644]
src/network/include/nl-helper.h [new file with mode: 0644]
src/network/include/notification.h [new file with mode: 0644]
src/network/include/protocol-info.h [new file with mode: 0644]
src/network/include/restriction-handler.h [new file with mode: 0644]
src/network/include/restriction-helper.h [new file with mode: 0644]
src/network/include/roaming.h [new file with mode: 0644]
src/network/include/settings.h [new file with mode: 0644]
src/network/include/specific-trace.h [new file with mode: 0644]
src/network/include/storage.h [new file with mode: 0644]
src/network/include/tethering-restriction.h [new file with mode: 0644]
src/network/join-dummy.c [new file with mode: 0644]
src/network/join.c [new file with mode: 0644]
src/network/ktgrabber-restriction.c [new file with mode: 0644]
src/network/main.c [new file with mode: 0644]
src/network/net-activity.c [new file with mode: 0644]
src/network/net-cls-cgroup.c [new file with mode: 0644]
src/network/network-dummy.c [new file with mode: 0644]
src/network/network.c [new file with mode: 0644]
src/network/network.conf [new file with mode: 0644]
src/network/nf-restriction.c [new file with mode: 0644]
src/network/nfacct-rule.c [new file with mode: 0644]
src/network/nl-helper.c [new file with mode: 0644]
src/network/notification.c [new file with mode: 0644]
src/network/options.c [new file with mode: 0644]
src/network/protocol-info.c [new file with mode: 0644]
src/network/reset.c [new file with mode: 0644]
src/network/restriction-handler.c [new file with mode: 0644]
src/network/restriction-helper.c [new file with mode: 0644]
src/network/restriction-local.c [new file with mode: 0644]
src/network/restriction.c [new file with mode: 0644]
src/network/roaming.c [new file with mode: 0644]
src/network/settings.c [new file with mode: 0644]
src/network/specific-trace.c [new file with mode: 0644]
src/network/storage.c [new file with mode: 0644]
src/network/tethering-restriction.c [new file with mode: 0644]
src/network/update.c [new file with mode: 0755]
src/powertop-wrapper/CMakeLists.txt [new file with mode: 0644]
src/powertop-wrapper/header.html [new file with mode: 0644]
src/powertop-wrapper/powertop-wrapper.cpp [new file with mode: 0644]
src/powertop-wrapper/powertop-wrapper.h [new file with mode: 0644]
src/proc-stat/CMakeLists.txt [new file with mode: 0644]
src/proc-stat/include/proc-handler.h [new file with mode: 0644]
src/proc-stat/include/proc-main.h [new file with mode: 0644]
src/proc-stat/include/proc-monitor.h [new file with mode: 0644]
src/proc-stat/include/proc-noti.h [new file with mode: 0644]
src/proc-stat/include/proc-process.h [new file with mode: 0644]
src/proc-stat/proc-handler.c [new file with mode: 0644]
src/proc-stat/proc-main.c [new file with mode: 0644]
src/proc-stat/proc-monitor.c [new file with mode: 0644]
src/proc-stat/proc-noti.c [new file with mode: 0644]
src/proc-stat/proc-process.c [new file with mode: 0644]
src/proc-stat/proc-stat.c [new file with mode: 0644]
src/resourced/init.c [new file with mode: 0644]
src/resourced/init.h [new file with mode: 0644]
src/resourced/main.c [new file with mode: 0644]
src/swap/swap.c [new file with mode: 0644]
src/timer-slack/timer-slack.c [new file with mode: 0644]
src/timer-slack/timer-slack.conf [new file with mode: 0644]
src/timer-slack/timer-slack.h [new file with mode: 0644]
src/utils/cgroup-test.c [new file with mode: 0644]
src/utils/database.c [new file with mode: 0644]
src/utils/datausage-tool.c [new file with mode: 0644]
src/utils/hashtest.c [new file with mode: 0644]
src/utils/iface-test.c [new file with mode: 0644]
src/utils/inodeuser.c [new file with mode: 0644]
src/utils/monitored.c [new file with mode: 0644]
src/utils/portuser.c [new file with mode: 0644]
src/utils/proc-stat-test.c [new file with mode: 0644]
src/utils/test-net-activity.c [new file with mode: 0644]
src/utils/udp-client.c [new file with mode: 0644]
src/utils/udp-common.c [new file with mode: 0644]
src/utils/udp-common.h [new file with mode: 0644]
src/utils/udp-server.c [new file with mode: 0644]
src/vip-agent/vip-process.c [new file with mode: 0644]
src/vip-agent/vip-process.conf [new file with mode: 0644]
src/vip-agent/vip-process.h [new file with mode: 0644]
src/vip-agent/vip-release-agent.c [new file with mode: 0644]
system-resource.manifest [new file with mode: 0644]
system-resource.pc.in [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bd01a09
--- /dev/null
@@ -0,0 +1,116 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+SET(fw_name "system-resource")
+SET(RESOURCED resourced)
+SET(LIBS libs)
+SET(PROC-STAT proc-stat)
+SET(NETWORK rd-network)
+
+IF("${POWERTOP_MODULE}" STREQUAL "ON")
+SET(POWERTOP-WRAPPER powertop-wrapper)
+ENDIF()
+
+PROJECT(${fw_name})
+
+#Set visibility
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -fvisibility=hidden")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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})
+  SET(DEBUG_ENABLED 1)
+  SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -O0 -g -pg")
+  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_NAME_DEPRECATED ${fw_name})
+SET(PC_REQUIRED "glib-2.0
+                vconf
+                vconf-internal-keys
+                sqlite3
+                dlog
+                eina
+                edbus")
+
+SET(PC_PROVIDED_LIBS "-l${PROC-STAT} -l${RESOURCED}")
+
+IF("${NETWORK_MODULE}" STREQUAL "ON")
+       SET(PC_PROVIDED_LIBS "${PC_PROVIDED_LIBS} -l${NETWORK}")
+ENDIF()
+
+IF("${POWERTOP_MODULE}" STREQUAL "ON")
+  SET(PC_PROVIDED_LIBS "${PC_PROVIDED_LIBS} -l${POWERTOP-WRAPPER}" )
+ENDIF()
+
+SET(PC_CFLAGS -I\${includedir}/system)
+SET(VERSION ${FULLVER})
+
+CONFIGURE_FILE(
+    lib${RESOURCED}.pc.in
+    ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc
+    @ONLY
+)
+
+CONFIGURE_FILE(
+    ${fw_name}.pc.in
+    ${CMAKE_SOURCE_DIR}/${fw_name}.pc
+    @ONLY
+)
+
+#init variables with sources
+SET(DATA_DIR                    ${CMAKE_SOURCE_DIR}/data)
+SET(CMAKELISTS_DIR              ${CMAKE_SOURCE_DIR}/CMakeLists)
+SET(INCLUDE_COMMON_DIR          ${CMAKE_SOURCE_DIR}/src/common)
+SET(INCLUDE_PUBLIC_DIR          ${CMAKE_SOURCE_DIR}/include)
+SET(RESOURCED_INCLUDEDIR        ${INCLUDE_COMMON_DIR} ${INCLUDE_PUBLIC_DIR})
+
+SET(SOURCE_DIR                  ${CMAKE_SOURCE_DIR}/src)
+SET(UTILS_SOURCE_DIR            ${SOURCE_DIR}/utils)
+SET(RESOURCED_SOURCE_DIR        ${SOURCE_DIR}/resourced)
+SET(PROC-STAT_SOURCE_DIR        ${SOURCE_DIR}/proc-stat)
+IF("${POWERTOP_MODULE}" STREQUAL "ON")
+SET(POWERTOP-WRAPPER_SOURCE_DIR ${SOURCE_DIR}/powertop-wrapper)
+ENDIF()
+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(LOGGING_SOURCE_DIR         ${SOURCE_DIR}/logging)
+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)
+
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION lib/pkgconfig)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/${fw_name}.pc DESTINATION lib/pkgconfig)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system.d)
+
+FILE(MAKE_DIRECTORY ${RESOURCED}-cmake)
+CONFIGURE_FILE(${CMAKELISTS_DIR}/${RESOURCED}.txt ${RESOURCED}-cmake/CMakeLists.txt COPYONLY)
+
+
+ADD_SUBDIRECTORY(${PROC-STAT_SOURCE_DIR})
+ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/${RESOURCED}-cmake)
+IF("${POWERTOP_MODULE}" STREQUAL "ON")
+ADD_SUBDIRECTORY(${POWERTOP-WRAPPER_SOURCE_DIR})
+ENDIF()
+ADD_SUBDIRECTORY(${MEMPS_SOURCE_DIR})
+ADD_SUBDIRECTORY(${NETWORK_SOURCE_DIR})
diff --git a/CMakeLists/resourced.txt b/CMakeLists/resourced.txt
new file mode 100644 (file)
index 0000000..1bcb012
--- /dev/null
@@ -0,0 +1,264 @@
+CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
+
+#CMake doesnt' support same project name for several TARGETS
+# libresourced uses it
+PROJECT(resource_d)
+
+SET(CMAKE_EXTRA_INCLUDE_FILES unistd.h)
+
+INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR}
+  ${RESOURCED_SOURCE_DIR}
+  ${LOGGING_SOURCE_DIR}/include
+  ${MEMORY_SOURCE_DIR}
+  ${PROC-STAT_SOURCE_DIR}/include
+  ${APP-STAT_SOURCE_DIR}/include
+  ${NETWORK_SOURCE_DIR}/include)
+
+SET (HEADERS
+  ${NETWORK_SOURCE_DIR}/include/datausage-vconf-callbacks.h
+  ${NETWORK_SOURCE_DIR}/include/iface-cb.h
+  ${INCLUDE_COMMON_DIR}/config.h
+  ${INCLUDE_COMMON_DIR}/config-parser.h
+  ${INCLUDE_COMMON_DIR}/const.h
+  ${INCLUDE_COMMON_DIR}/daemon-options.h
+  ${INCLUDE_COMMON_DIR}/macro.h
+  ${INCLUDE_COMMON_DIR}/logging-common.h
+  ${INCLUDE_COMMON_DIR}/lowmem-common.h
+  ${INCLUDE_COMMON_DIR}/module-data.h
+  ${INCLUDE_COMMON_DIR}/module.h
+  ${INCLUDE_COMMON_DIR}/swap-common.h
+)
+
+SET (SOURCES
+  ${COMMON_SOURCE_DIR}/cgroup.c
+  ${COMMON_SOURCE_DIR}/notifier.c
+  ${COMMON_SOURCE_DIR}/appid-helper.c
+  ${COMMON_SOURCE_DIR}/config-parser.c
+  ${COMMON_SOURCE_DIR}/edbus-handler.c
+  ${COMMON_SOURCE_DIR}/file-helper.c
+  ${COMMON_SOURCE_DIR}/module-data.c
+  ${COMMON_SOURCE_DIR}/module.c
+  ${NETWORK_SOURCE_DIR}/counter.c
+  ${PROC-STAT_SOURCE_DIR}/proc-handler.c
+  ${PROC-STAT_SOURCE_DIR}/proc-main.c
+  ${PROC-STAT_SOURCE_DIR}/proc-noti.c
+  ${PROC-STAT_SOURCE_DIR}/proc-process.c
+  ${PROC-STAT_SOURCE_DIR}/proc-monitor.c
+  ${RESOURCED_SOURCE_DIR}/init.c
+  ${RESOURCED_SOURCE_DIR}/main.c
+  )
+
+INSTALL(FILES ${DATA_DIR}/${EXCLUDE_LIST_FILE_NAME} DESTINATION /usr/etc)
+#network module
+IF("${NETWORK_MODULE}" STREQUAL "ON")
+
+  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
+    )
+  ENDIF()
+
+  SET(SOURCES ${SOURCES}
+    ${NETWORK_SOURCE_DIR}/counter-process.c
+    ${NETWORK_SOURCE_DIR}/daemon-options.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}/notification.c
+    ${NETWORK_SOURCE_DIR}/restriction-handler.c
+    ${NETWORK_SOURCE_DIR}/restriction-helper.c
+    ${NETWORK_SOURCE_DIR}/restriction-local.c
+    ${NETWORK_SOURCE_DIR}/tethering-restriction.c
+    )
+
+   IF ("${TETHERING_FEATURE}" STREQUAL "ON")
+       SET(TETHERING_FEATURE 1)
+   ENDIF()
+
+   INSTALL(FILES ${NETWORK_SOURCE_DIR}/network.conf
+               DESTINATION /etc/resourced)
+
+ENDIF()
+
+IF("${VIP_AGENT}" STREQUAL "ON")
+  SET(SOURCES ${SOURCES}
+     ${VIP_SOURCE_DIR}/vip-process.c
+    )
+ENDIF()
+
+#logging module
+IF("${LOGGING_MODULE}" STREQUAL "ON")
+  SET(SOURCES ${SOURCES}
+    ${LOGGING_SOURCE_DIR}/logging.c
+    )
+  ADD_DEFINITIONS("-DLOGGING_SUPPORT")
+#logging memory
+  IF("${LOGGING_MEMORY}" STREQUAL "ON")
+    SET(SOURCES ${SOURCES}
+      ${MEMORY_SOURCE_DIR}/logging-memory.c
+      )
+  ENDIF()
+#logging cpu
+  IF("${LOGGING_CPU}" STREQUAL "ON")
+    SET(SOURCES ${SOURCES}
+      ${CPU_SOURCE_DIR}/logging-cpu.c
+      )
+  ENDIF()
+ENDIF()
+
+#memory module
+IF("${MEMORY_MODULE}" STREQUAL "ON")
+  SET(SOURCES ${SOURCES}
+    ${MEMORY_SOURCE_DIR}/lowmem-dbus.c
+  )
+  IF("${MEMORY_CGROUP}" STREQUAL "ON")
+    SET(SOURCES ${SOURCES}
+      ${MEMORY_SOURCE_DIR}/vmpressure-lowmem-handler.c
+    )
+  ELSE()
+    SET(SOURCES ${SOURCES}
+      ${MEMORY_SOURCE_DIR}/lowmem-handler.c
+    )
+  ENDIF()
+  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()
+
+SET (REQUIRES_LIST ${REQUIRES_LIST}
+       ecore
+       dlog
+       glib-2.0
+        sqlite3
+       vconf
+       vconf-internal-keys
+       ecore-file
+       eina
+       edbus
+       libsystemd-daemon
+       journal
+)
+
+IF("${LOGGING_MODULE}" STREQUAL "ON")
+  SET (REQUIRES_LIST ${REQUIRES_LIST}
+       libsystemd-journal
+  )
+ENDIF()
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(daemon_pkgs REQUIRED ${REQUIRES_LIST})
+
+FOREACH(flag ${daemon_pkgs_CFLAGS})
+        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag} -pthread")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+CONFIGURE_FILE(${INCLUDE_COMMON_DIR}/config.h.in
+  ${INCLUDE_COMMON_DIR}/config.h)
+
+ADD_EXECUTABLE(hashtest ${UTILS_SOURCE_DIR}/hashtest.c)
+TARGET_LINK_LIBRARIES(hashtest ${daemon_pkgs_LDFLAGS})
+
+ADD_EXECUTABLE(cgroup-test ${UTILS_SOURCE_DIR}/cgroup-test.c
+       ${COMMON_SOURCE_DIR}/cgroup.c
+       ${COMMON_SOURCE_DIR}/file-helper.c
+       ${COMMON_SOURCE_DIR}/appid-helper.c
+       ${NETWORK_SOURCE_DIR}/net-cls-cgroup.c
+       )
+TARGET_LINK_LIBRARIES(cgroup-test ${daemon_pkgs_LDFLAGS} "-L/lib/ -lrt")
+
+ADD_EXECUTABLE (${PROJECT_NAME} ${HEADERS} ${SOURCES})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+  ${daemon_pkgs_LDFLAGS}
+)
+
+IF("${NETWORK_MODULE}" STREQUAL "ON")
+  TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+    resourced
+    app-stat
+    storage
+    settings
+    net-cls
+    roaming
+    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 ${daemon_pkgs_LDFLAGS})
+
+  ENDIF()
+
+  INSTALL(FILES ${DATA_DIR}/traffic_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(PROGRAMS ${CMAKE_BINARY_DIR}/scripts/resourced-zram.sh DESTINATION bin)
+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 dlog)
+INSTALL(TARGETS vip-release-agent DESTINATION /usr/bin)
+INSTALL(FILES ${VIP_SOURCE_DIR}/vip-process.conf DESTINATION /etc/resourced RENAME vip-process.conf)
+ENDIF()
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..5554685
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,204 @@
+                                 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
diff --git a/data/resourced_proc_exclude.ini b/data/resourced_proc_exclude.ini
new file mode 100644 (file)
index 0000000..801f014
--- /dev/null
@@ -0,0 +1,14 @@
+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
+w-home
diff --git a/data/traffic_db.sql b/data/traffic_db.sql
new file mode 100644 (file)
index 0000000..628e669
--- /dev/null
@@ -0,0 +1,66 @@
+PRAGMA journal_mode = PERSIST;
+
+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,
+  PRIMARY KEY (binpath, time_stamp, iftype)
+);
+
+CREATE INDEX IF NOT EXISTS binpath_st_idx ON statistics(binpath, iftype);
+
+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,
+  PRIMARY KEY(binpath, iftype, roaming)
+);
+
+CREATE INDEX IF NOT EXISTS binpath_qt_idx ON quotas(binpath, iftype);
+
+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,
+  PRIMARY KEY (binpath, iftype, start_time, finish_time, roaming)
+);
+
+CREATE INDEX IF NOT EXISTS binpath_effective_quotas_idx ON effective_quotas(binpath, iftype);
+
+CREATE TABLE IF NOT EXISTS restrictions (
+  binpath TEXT,
+  rcv_limit BIGINT,
+  send_limit BIGINT,
+  iftype INT,
+  rst_state INT,
+  quota_id INT,
+  roaming INT,
+  PRIMARY KEY (binpath, iftype)
+);
+
+CREATE INDEX IF NOT EXISTS binpath_restrictions_idx ON restrictions(binpath, iftype);
+
+CREATE TABLE IF NOT EXISTS iface_status (
+  update_time BIGINT,
+  iftype INT,
+  ifstatus INT,
+  PRIMARY KEY (update_time)
+);
+
+CREATE INDEX IF NOT EXISTS update_tm_if_idx ON iface_status(update_time, iftype, ifstatus);
diff --git a/docs/description.txt b/docs/description.txt
new file mode 100644 (file)
index 0000000..acecd66
--- /dev/null
@@ -0,0 +1,14 @@
+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.
diff --git a/docs/func.txt b/docs/func.txt
new file mode 100644 (file)
index 0000000..b1ce404
--- /dev/null
@@ -0,0 +1,427 @@
+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_pkg_name(const char *pkg_name, int create)
+
+Converts application package name 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.
+
diff --git a/docs/traffic_db.txt b/docs/traffic_db.txt
new file mode 100644 (file)
index 0000000..4b5cd0d
--- /dev/null
@@ -0,0 +1,69 @@
+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
diff --git a/include/data_usage.h b/include/data_usage.h
new file mode 100755 (executable)
index 0000000..45bc817
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * 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;
+} resourced_net_restrictions;
+
+/**
+ * @brief the same as for restriction
+ */
+typedef struct {
+       long incoming_bytes;
+       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
+               system-resource 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 system-resource 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;
+       resourced_iface_type iftype;
+       time_t *start_time;
+       resourced_roaming_type roaming_type;
+} 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;
+       resourced_iface_type iftype;
+       resourced_roaming_type roaming;
+};
+
+/**
+ * @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;
+       resourced_iface_type iftype;
+       resourced_tm_interval *interval;
+       resourced_common_info foreground;
+       resourced_common_info background;
+       resourced_roaming_type roaming;
+       resourced_hw_net_protocol_type hw_net_protocol_type;
+} 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_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;
+       resourced_iface_type iftype;
+       resourced_restriction_state rst_state;
+       int rcv_limit;
+       int send_limit;
+       int quota_id;
+       resourced_roaming_type roaming;
+} 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;
+       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);
+
+
+
+/**
+ * @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);
+
+/**
+ * @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_ */
diff --git a/include/powertop-dapi.h b/include/powertop-dapi.h
new file mode 100644 (file)
index 0000000..70afdb1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Library for getting power usage statistics
+ *
+ * Copyright (c) 2000 - 2012 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 __POWERTOP_DAPI_H__
+#define __POWERTOP_DAPI_H__
+
+#ifndef UNUSED
+#define UNUSED __attribute__((unused))
+#endif /* UNUSED */
+
+#ifndef DEPRECATED
+#define DEPRECATED __attribute__((deprecated))
+#endif
+
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void powertop_set_check_interval(unsigned int interval) DEPRECATED;
+bool powertop_start_check(const char *output_path) DEPRECATED;
+void powertop_stop_check(void) DEPRECATED;
+void powertop_async_stop_check(void (*callback)(void *), void *arg) DEPRECATED;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __POWERTOP_DAPI_H__ */
diff --git a/include/proc_stat.h b/include/proc_stat.h
new file mode 100644 (file)
index 0000000..fa759b7
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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,
+};
+
+
+/**
+ * @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 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__ */
diff --git a/include/rd-network.h b/include/rd-network.h
new file mode 100755 (executable)
index 0000000..fbb14b1
--- /dev/null
@@ -0,0 +1,936 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef __RD_NETWORK_H__
+#define __RD_NETWORK_H__
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Enumeration for return type
+ */
+typedef enum {
+       NETWORK_ERROR_NONMONITOR = -9,          /** < Process don't show watchdog popup */
+       NETWORK_ERROR_NOTIMPL = -7,              /**< Not implemented yet error */
+       NETWORK_ERROR_UNINITIALIZED = -6,        /**< Cgroup doen't mounted or daemon not started */
+       NETWORK_ERROR_NO_DATA = -5,              /**< Success, but no data */
+       NETWORK_ERROR_INVALID_PARAMETER = -4,/**< Invalid parameter */
+       NETWORK_ERROR_OUT_OF_MEMORY = -3,        /**< Out of memory */
+       NETWORK_ERROR_DB_FAILED = -2,    /**< Database error */
+       NETWORK_ERROR_FAIL = -1,                 /**< General error */
+       NETWORK_ERROR_NONE = 0           /**< General success */
+} network_error_e;
+
+
+/**
+ * @brief Enumeration for return type of the callback
+ */
+typedef enum {
+       NETWORK_CANCEL = 0,                     /**< cancel */
+       NETWORK_CONTINUE = 1,           /**< continue */
+} network_cb_ret_e;
+
+/**
+ * @brief Enumeration for the monitored process state
+ */
+typedef enum {
+       NETWORK_STATE_UNKNOWN = 0,
+       NETWORK_STATE_FOREGROUND = 1 << 1,              /** < foreground state */
+       NETWORK_STATE_BACKGROUND = 1 << 2,              /** < background state */
+       NETWORK_STATE_LAST_ELEM = 1 << 3
+} network_state_e;
+
+/**
+ * @brief Enumeration for network restriction state
+ */
+typedef enum {
+       NETWORK_RESTRICTION_UNDEFINDED,
+       NETWORK_RESTRICTION_ACTIVATED,  /** < restriction activated - means it
+                                               was sent to kernel */
+       NETWORK_RESTRICTION_EXCLUDED,   /** < restriction has been excluded -
+                                               means it was sent to kernel as
+                                               excluded */
+       NETWORK_RESTRICTION_REMOVED,     /** < restriction has been removed */
+
+       NETWORK_RESTRICTION_MAX_VALUE
+} network_restriction_state;
+
+/**
+ * @brief Enumeration for network interface types
+ */
+typedef enum {
+       NETWORK_IFACE_UNKNOWN,  /**< undefined iface */
+       NETWORK_IFACE_DATACALL, /**< mobile data */
+       NETWORK_IFACE_WIFI,             /**< wifi data */
+       NETWORK_IFACE_WIRED,    /**< wired interface */
+       NETWORK_IFACE_BLUETOOTH,        /**< bluetooth interface */
+       NETWORK_IFACE_ALL,      /**< enumerate all network interface types */
+       NETWORK_IFACE_LAST_ELEM
+} network_iface_e;
+
+/**
+ * @brief Structure for time interval
+ * @details It's time interval. Zero interval since 0 til 0 means entires interval.
+ */
+typedef struct {
+       time_t from;
+       time_t to;
+} network_tm_interval_s;
+
+/**
+ * @brief Enumeration for network connection period type
+ * @details Last received/sent mean counting data from the first connection of each interface
+ */
+typedef enum {
+       NETWORK_CON_PERIOD_UNKNOWN,                     /**< Undefined period */
+       NETWORK_CON_PERIOD_LAST_RECEIVED_DATA,  /**< Last received data */
+       NETWORK_CON_PERIOD_LAST_SENT_DATA,              /**< Last sent data */
+       NETWORK_CON_PERIOD_TOTAL_RECEIVED_DATA, /**< Total received data */
+       NETWORK_CON_PERIOD_TOTAL_SENT_DATA,             /**< Total sent data */
+       NETWORK_CON_PERIOD_LAST_ELEM
+} network_connection_period_e;
+
+/**
+ * @brief Enumeration for network roaming type
+ */
+typedef enum {
+       NETWORK_ROAMING_UNKNOWN,                /**< can't define roaming - roaming unknown */
+       NETWORK_ROAMING_ENABLE,         /**< in roaming */
+       NETWORK_ROAMING_DISABLE,                /**< not in roaming */
+       NETWORK_ROAMING_LAST_ELEM,
+} network_roaming_e;
+
+/**
+ * @brief Enumeration for hardware network protocol types
+ */
+typedef enum {
+       NETWORK_PROTOCOL_NONE,                  /**< Network unknown */
+       NETWORK_PROTOCOL_DATACALL_NOSVC,                /**< Network no service */
+       NETWORK_PROTOCOL_DATACALL_EMERGENCY,            /**< Network emergency */
+       NETWORK_PROTOCOL_DATACALL_SEARCH,               /**< Network search 1900 */
+       NETWORK_PROTOCOL_DATACALL_2G,                   /**< Network 2G */
+       NETWORK_PROTOCOL_DATACALL_2_5G,         /**< Network 2.5G */
+       NETWORK_PROTOCOL_DATACALL_2_5G_EDGE,            /**< Network EDGE */
+       NETWORK_PROTOCOL_DATACALL_3G,                   /**< Network UMTS */
+       NETWORK_PROTOCOL_DATACALL_HSDPA,                /**< Network HSDPA */
+       NETWORK_PROTOCOL_DATACALL_LTE,          /**< Network LTE */
+       NETWORK_PROTOCOL_MAX_ELEM
+} network_hw_net_protocol_e;
+
+/**
+ * @brief Enumeration for the boolean option
+ * @details Description of the boolean option for enabling/disabling
+ *     network interfaces and enabling/disabling some behaviar
+ */
+typedef enum {
+       NETWORK_OPTION_UNDEF,
+       NETWORK_OPTION_ENABLE,
+       NETWORK_OPTION_DISABLE
+} network_option_e;
+
+/**
+ * @brief Structure for network option
+ * @details Set of the options.
+ * version - contains structure version
+ * wifi - enable/disable wifi, NETWORK_OPTION_UNDEF to leave option as is
+ * datacall - enable/disable datacall, NETWORK_OPTION_UNDEF to leave option as is
+ * network_timer - set period of the updating data from the kernel,
+ *     0 to leave option as is
+ * datacall_logging - enable/disable datacall_logging,
+ *     NETWORK_OPTION_UNDEF to leave option as is
+ */
+typedef struct {
+       unsigned char version;
+       network_option_e wifi;
+       network_option_e datacall;
+       time_t network_timer;
+       network_option_e datacall_logging;
+} network_option_s;
+
+/**
+ * @brief Set options, daemon will handle option setting.
+ * @param[in] options The network state option
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_option_s
+ * @see #network_get_option
+ */
+network_error_e network_set_option(const network_option_s *options);
+
+/**
+ * @brief Get performance control options.
+ * @param[out] options The network state option
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_option_s
+ * @see #network_set_option
+ */
+network_error_e network_get_option(network_option_s *options);
+
+/**
+ * @brief Make cgroup and put in it the given pid and generated classid
+ * @details If cgroup already exists function just put pid in it.
+ * @param[in] pid Process, that will be added to cgroup pkg name
+ * @param[in] pkg_name Package name
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_get_classid_by_pkg_name
+ */
+network_error_e network_make_cgroup_with_pid(const int pid,
+       const char *pkg_name);
+
+/**
+ * @brief Get classid from cgroup with name pkg_name
+ * @param[in] pkg_name Name of the cgroup
+ * @param[in] create In case of true - create cgroup if it's not exists
+ * @return a positive value is classid, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_make_cgroup_with_pid
+ */
+u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create);
+
+/**
+ * @brief Structure for network restriction information
+ * @details
+ * rs_type: foreground or background process
+ * iftype - interface type to apply restriction
+ * send_limit - amount number of engress bytes allowed for restriction
+ * rcv_limit - amount number of ingress bytes allowed for restriction
+ *             old behaviour for send_limit & rcv_limit was 0
+ * snd_warning_limit - threshold for warning notification on engress bytes
+ * rcv_warning_limit - threshold for warning notification on ingress bytes
+ *             value - WARNING_THRESHOLD_UNDEF means no threshold
+ *             this limit is different from quota warning threshold,
+ *             threshold means remaining, limit means occupaied
+ */
+typedef struct {
+       network_state_e rs_type;
+       network_iface_e iftype;
+       int send_limit;
+       int rcv_limit;
+       int snd_warning_limit;
+       int rcv_warning_limit;
+} network_restriction_s;
+
+/**
+ * @brief Enumeration for restriction counter
+ */
+typedef struct {
+       long incoming_bytes;
+       long outgoing_bytes;
+} network_counter_s;
+
+/**
+ * @brief Enumeration for holding data usage information
+ */
+typedef struct {
+       network_counter_s cnt;
+       network_restriction_s rst;
+} network_common_info;
+
+/**
+ * @brief Set restriction information
+ * @details Set and apply restriction for application.
+ * It will create new restriction or modify existing.
+ * @param[in] app_id Application identifier, it's package name now
+ * @param[in] restriction Restriction to apply for application in foreground mode
+ * At least one of the restriction should be setted.
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_restriction_s
+ * @see #network_remove_restriction
+ * @see #network_remove_restriction_by_iftype
+ */
+network_error_e network_set_restriction(const char *app_id,
+                           const network_restriction_s *restriction);
+
+/**
+ * @brief Structure for information on restrictions.
+ * @details
+ * app_id - application identification - copy it if you in
+ * callback function, don't store raw pointer on it
+ * iftype - type of network interface
+ */
+typedef struct {
+       const char *app_id;
+       network_iface_e iftype;
+       network_restriction_state rst_state;
+       int rcv_limit;
+       int send_limit;
+} network_restriction_info_s;
+
+/**
+ * @brief callback for processing information of restrictions
+ */
+typedef network_cb_ret_e(*network_restriction_cb)(
+       const network_restriction_info_s *info, void *user_data);
+
+/**
+ * @brief Restrictions enumerate function
+ * @param[in] restriction_db The callback is called for each application that restrcited now
+ * @param[in] user_data User data will be passed to the callback function
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_restriction_cb
+ */
+network_error_e network_restriction_foreach(network_restriction_cb restriction_cb,
+                               void *user_data);
+
+/**
+ * @brief Remove existing restriction for application
+ * @details Remove existing restriction for application
+ *   It will delete restriction rule in kernel
+ * @param[in] app_id Application identifier, it's package name
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_set_restriction
+ * @see #network_remove_restriction_by_iftype
+ */
+network_error_e network_remove_restriction(const char *app_id);
+
+/**
+ * @brief Remove existing restriction for application from interface type
+ * @details Remove existing restriction for application
+ *   It will delete restriction rule in kernel
+ * @param[in] app_id Application identifier, it's package name
+ * @param[in] iftype Interface type
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_iface_e
+ * @see #network_set_restriction
+ * @see #network_remove_restriction_by_iftype
+ */
+network_error_e network_remove_restriction_by_iftype(const char *app_id,
+                                            const network_iface_e iftype);
+
+/**
+ * @brief Exclude restriction for application
+ * @details Exclude restriction for application
+ *   It will exclude restriction rule in kernel
+ * @param[in] app_id Application identifier, it's package name
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_OK Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_set_restriction
+ * @see #network_exclude_restriction_by_iftype
+ */
+network_error_e network_exclude_restriction(const char *app_id);
+
+/**
+ * @brief Exclude restriction for application from interface type
+ * @details Exclude restriction for application
+ *   It will exclude restriction rule in kernel
+ * @param[in] app_id Application identifier, it's package name
+ * @param[in] iftype Interface type
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_OK Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_iface_e
+ * @see #network_set_restriction
+ * @see #network_exclude_restriction_by_iftype
+ */
+network_error_e network_exclude_restriction_by_iftype(
+       const char *app_id, const network_iface_e iftype);
+
+/**
+ * @brief Structure for network activity information
+ */
+typedef struct {
+       int type;               /*<< ingress/egress */
+       char *appid;
+       int iftype;
+       int bytes;
+} network_activity_info_s;
+
+/**
+ * @brief callback for network activity information of packet
+ */
+typedef network_cb_ret_e(*network_activity_cb)(network_activity_info_s *info);
+
+/**
+ * @brief Register activity callback
+ * @details This function registering callback which invokes per every packet.
+ * Function creates new reading thread and returns.
+ * @param[in] activity_cb Invoked per every packet with NET_ACTIVITY channel
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_activity_cb
+ */
+network_error_e network_register_activity_cb(network_activity_cb activity_cb);
+
+/**
+ * @brief After invoking this function, application will be in the monitored scope
+ * @details It creates an appropriate cgroup,
+ * it generates classid for the network performance control.
+ * It creates a unit file for the systemd.
+ * @param[in] app_id Application identifier, it's package name now
+ * @param[in] pid Pid to put in to cgroup, or self pid of 0
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ */
+network_error_e network_join_app_performance(const char *app_id, const pid_t pid);
+
+/**
+ * @brief Update the resourced counters and stores it in the database.
+ * @details Updates going asynchronyusly, it mean client can't be sure was
+ * counters updated or not after this function finished.
+ * To handle finish of the update process client need to
+ * regist callback function
+ * @see network_register_update_cb.
+ * Next counters updating will procced according to resourced
+ * update period, unless another network_update_statisitcs is
+ * not invoked.
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ */
+network_error_e network_update_statistics(void);
+
+/*
+ * @brief Counters update information
+ * @details This structure is needed to prevent client API from modification
+ * in case of any information about update will be required.
+ */
+struct network_update_info {
+       /*dummy content*/
+};
+
+/**
+ * @brief Callback for update counters
+ */
+typedef network_cb_ret_e(*network_update_cb)(
+       const struct network_update_info *info,
+       void *user_data);
+
+/**
+ * @brief Register callback for update counters.
+ * @details Callback function will be called if
+ * network_update_statistics is requested.
+ * To stop callbacks invocation return NETWORK_CANCEL from
+ * callback function or call @see network_unregister_update_cb.
+ *
+ * @param[in] user_data pointer to any data given to callback function.
+ *     Memory area should not be released until callback is unregistered.
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @code
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rd-network.h>
+
+network_cb_ret_e network_update(const struct network_update_info *info,
+       void *user_data)
+{
+       char buf[50];
+       char *str_data = user_data;
+       printf("Callback updated. Stop yes/no?: ");
+       scanf("%s", buf);
+       if (strcmp(buf, "yes") == 0)
+               return NETWORK_CANCEL;
+
+       printf("user data is %s\n", user_data);
+
+       return NETWORK_CONTINUE;
+}
+
+int main(void)
+{
+       network_error_e ret;
+
+       ecore_init();
+
+       char *user_data = (char *)malloc(1024);
+
+       strncpy(user_data, "hello", 1023);
+
+       ret = network_register_update_cb(network_update, (void *)user_data);
+
+       network_update_statistics();
+
+       ecore_main_loop_begin();
+
+       free(user_data);
+       ecore_shutdown();
+}
+
+ * @endcode
+ */
+network_error_e network_register_update_cb(network_update_cb update_cb,
+       void *user_data);
+
+/**
+ * @brief Unregister update callback.
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ */
+void network_unregister_update_cb(void);
+
+/**
+ * @brief Structure for selection rule applied
+ */
+typedef struct {
+       unsigned char version;
+       time_t from;
+       time_t to;
+       network_iface_e iftype;
+       int granularity;
+} network_selection_rule_s;
+
+/**
+ * @brief Bundle structure for bringing all together application identification and properties
+ * @details
+ * app_id - application identification - copy it if you in
+ *   callback function, don't store raw pointer on it
+ * iface - interface name, NULL means all interfaces,
+ *   don't store raw pointer on it in the callback function, copy it by value
+ * interval - time interval for given result, NULL means entire interval
+ * foreground - foreground restrictions and counters
+ * background - background restrictions and counters
+ */
+typedef struct {
+       const char *app_id;
+       network_iface_e iftype;
+       network_tm_interval_s *interval;
+       network_common_info foreground;
+       network_common_info background;
+       network_roaming_e roaming;
+       network_hw_net_protocol_e hw_net_protocol_type;
+} network_info_s;
+
+/**
+ * @brief Callback for enumerate counters and restrictions
+ */
+typedef network_cb_ret_e(*network_info_cb) (const network_info_s *info,
+                                              void *user_data);
+
+/**
+ * @brief Data usage enumerate function
+ * @details The callback is called for each application that used network
+ * in between timestamps specified.
+ * If interface name is not specified, each application will only appear
+ * once with the total traffic used over all interfaces.
+ * @param[in] rule Selection rule
+ * @param[in] info_cb The callback is called for each application
+ * that used network in between timestamps specified
+ * @param[in] user_data User data will be passed to the callback function
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_selection_rule_s
+ * @see #network_info_cb
+ * @see #network_details_foreach
+ */
+network_error_e network_foreach(const network_selection_rule_s *rule,
+                            network_info_cb info_cb, void *user_data);
+
+/**
+ * @brief Data usage details enumerate function
+ * @detail
+ * If interface name is specified in rule, the callback will be called
+ * exactly 1 time with the total traffic counts for that interface
+ * by specified application in the specified time interval.
+ * If interface name is not specified, the callback will be called once
+ * for each interface used by application during the specified interval.
+ * It could be 0 if the application did not use any network interfaces
+ * during that period.
+ * @param[in] app_id Application id
+ * @param[in] rule Selection rule
+ * @param[in] info_cb The callback is called for each application
+ * that used network in between timestamps specified
+ * @param[in] user_data User data will be passed to the callback function
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_selection_rule_s
+ * @see #network_info_cb
+ * @s22 #network_foreach
+ */
+network_error_e network_details_foreach(const char *app_id,
+                                          network_selection_rule_s *rule,
+                                          network_info_cb info_cb,
+                                          void *user_data);
+
+/**
+ * @brief Structure for reset rule
+ * @details It's statistics erasing description.
+ * app_id - Erase statistics per appropriate app_id.
+ * app_id can be NULL in this case erasing all datas
+ * iftype - Erase statistics per appropriate network interface type
+ * #network_iface_e, if iftype is NETWORK_IFACE_LAST_ELEM - erase all
+ * NETWORK_IFACE_UNKNOW - means undetermined interface on moment of storing data.
+ * interval - It's time interval, @see network_tm_interval_s.
+ * It should be set. Zero interval since 0 till 0 means entire interval.
+ * connection_state - It's mask on time interval.
+ * Possible variation LAST and TOTAL for send and received data.
+ */
+typedef struct {
+       unsigned char version;
+       char *app_id;
+       network_iface_e iftype;
+       network_tm_interval_s *interval;
+       network_connection_period_e connection_state;
+} network_reset_rule_s;
+
+/**
+ * @brief Reset data usage information
+ * @param[in] rule Reset rule. It's statistics erasing description
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_reset_rule_s
+ */
+network_error_e network_reset(const network_reset_rule_s *rule);
+
+/**
+ * @brief Reset filter for quota
+ * @details
+ * app_id is mandatory field
+ * iftype interface type, NETWORK_IFACE_UNKNOWN
+ * interface is not valid parameter, use NETWORK_IFACE_ALL instead
+ * roaming_type roaming type
+ * If user will not specify last 2 fields (UNKNOWN by default),
+ *   neither quota with defined interface nor
+ *   quota with defined roaming state will be removed.
+ */
+typedef struct {
+       const char *app_id;
+       network_iface_e iftype;
+       network_roaming_e roaming;
+} network_quota_reset_rule_s;
+
+/**
+ * @brief Remove datausage quota by quota rule
+ * @param[in] rule reset filter for quota
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_quota_reset_rule_s
+ * @see #network_set_quota
+ * @see #network_remove_quota_by_iftype
+ */
+network_error_e network_remove_quota(
+       const network_quota_reset_rule_s *rule);
+
+/**
+ * @brief Remove datausage quota by quota rule
+ * @param[in] app_id Application id
+ * @param[in] iftype Interface type
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_quota_reset_rule_s
+ * @see #network_set_quota
+ * @see #network_remove_quota
+ */
+network_error_e network_remove_quota_by_iftype(
+       const char *app_id, const network_iface_e iftype);
+
+/**
+ * @brief Datausage quota
+ * @details
+ * time_period - time interval for quota, use predefined quota
+ * #network_quota_s_period_t
+ * snd_quota - quota for outcoming data
+ * rcv_quota - quota for incoming data
+ * warning_send_threshold - threshold for warning notification on engress bytes
+ * warning_rcv_threshold - threshold for warning notification on ingress bytes
+ * value - WARNING_THRESHOLD_UNDEF means no threshold
+ *       - WARNING_THRESHOLD_DEFAULT means system-resource will be
+ *         responsible for evaluation threshold value
+ * The threshold value is amount of bytes remaining till blocking
+ * quota_type - at present it can be foreground quota or background
+ * iftype - network interface type
+ * start_time - quota processing activation time, if NULL current time is used
+ */
+typedef struct {
+       int time_period;
+       int64_t snd_quota;
+       int64_t rcv_quota;
+       int snd_warning_threshold;
+       int rcv_warning_threshold;
+       network_state_e quota_type;
+       network_iface_e iftype;
+       time_t *start_time;
+       network_roaming_e roaming_type;
+} network_quota_s;
+
+/**
+ * @brief Set datausage quota
+ * @details This function will set time interval based quota for data usage
+ * Restriction will be applied in case of exceeding of the quota
+ * during time interval
+ * @param app_id[in] Application identifier, it's package name
+ * @param quotas[in] Time interval based restriction for data usage
+ *
+ * @return 0 on success, otherwise a negative error value
+ * @retval #NETWORK_ERROR_NONE Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #NETWORK_ERROR_NO_DATA Success, but no data
+ * @retval #NETWORK_ERROR_UNINITIALIZED Cgroup doesn't mounted or daemon is not started
+ * @retval #NETWORK_ERROR_NOTIMPL No implemented yet error
+ * @retval #NETWORK_ERROR_NONMONITOR Process don't show watchdog popup
+ *
+ * @see #network_set_quota
+ * @see #network_remove_quota
+ * @see #network_remove_quota_by_iftype
+ */
+network_error_e network_set_quota(const char *app_id,
+                             const network_quota_s *quota);
+
+
+/**
+ *
+ * This function get restriction state.
+ * State can be following:
+ *     NETWORK_RESTRICTION_UNDEFINDED - means restriction wasn't set
+ *     NETWORK_RESTRICTION_ACTIVATED  - means  restriction activated
+ *     NETWORK_RESTRICTION_EXCLUDED   - restriction has been excluded
+ *
+ * @code
+ * #include <rd-network.h>
+ *
+ * int is_whole_network_restricted()
+ * {
+ *     network_restriction_state state;
+ *     network_error_r ret_code = network_get_restriction_state(
+ *             RESOURCED_ALL_APP, NETWORK_IFACE_ALL, &state);
+ *     if (ret_code != NETWORK_ERROR_NONE &&
+ *             state == NETWORK_RESTRICTION_ACTIVATED)
+ *             return 1;
+ *     return 0;
+ * }
+ *
+ * @endcode
+ *
+ * @retval #NETWORK_ERROR_OK Successful
+ * @retval #NETWORK_ERROR_FAIL General error
+ * @retval #NETWORK_ERROR_DB_FAILED Database error
+ * @retval #NETWORK_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see #network_iface_e
+ * @see #network_restriction_state
+ * @see #network_set_restriction
+ * @see #network_exclude_restriction_by_iftype
+ *
+ */
+network_error_e network_get_restriction_state(const char *pkg_id,
+       network_iface_e iftype, network_restriction_state *state);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // __RD_NETWORK_H__
diff --git a/include/resourced.h b/include/resourced.h
new file mode 100644 (file)
index 0000000..d9031a9
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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>
+
+#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_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,      /**< 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_ */
diff --git a/libresourced.manifest b/libresourced.manifest
new file mode 100644 (file)
index 0000000..3256181
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+<request>
+       <domain name="_"/>
+</request>
+</manifest>
diff --git a/libresourced.pc.in b/libresourced.pc.in
new file mode 100644 (file)
index 0000000..bf25f8a
--- /dev/null
@@ -0,0 +1,15 @@
+# Package Information for pkg-config
+#
+# Copyright (c) 2014 Samsung Electronics Co., Ltd.
+# All rights reserved.
+#
+
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: @PC_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_PROVIDED_LIBS@
+Cflags: @PC_CFLAGS@
diff --git a/packaging/add_test_net_activity.patch b/packaging/add_test_net_activity.patch
new file mode 100644 (file)
index 0000000..df5c1ef
--- /dev/null
@@ -0,0 +1,44 @@
+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
diff --git a/packaging/resourced-cpucgroup.service b/packaging/resourced-cpucgroup.service
new file mode 100644 (file)
index 0000000..8db5ad7
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=make cpucgroup
+After=graphical.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/resourced-cpucgroup.sh
+
+[Install]
+WantedBy=graphical.target
diff --git a/packaging/resourced-logging.service b/packaging/resourced-logging.service
new file mode 100644 (file)
index 0000000..fe0b64d
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Logging Initialization
+After=resourced.service
+
+[Service]
+Type=oneshot
+ExecStartPre=/bin/sleep 1
+ExecStart=/usr/bin/dbus-send --type=signal --system /Org/Tizen/ResourceD/Logging org.tizen.resourced.logging.LoggingInit
+
+[Install]
+WantedBy=graphical.target
diff --git a/packaging/resourced-zram.service b/packaging/resourced-zram.service
new file mode 100644 (file)
index 0000000..0ee4c1c
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=prepare zram
+After=default.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/resourced-zram.sh
+
+[Install]
+WantedBy=graphical.target
diff --git a/packaging/resourced.service b/packaging/resourced.service
new file mode 100644 (file)
index 0000000..c9454bc
--- /dev/null
@@ -0,0 +1,19 @@
+[Unit]
+Description=Resource management daemon
+After=tizen-system.target
+Wants=tizen-system.target
+
+[Service]
+Type=notify
+EnvironmentFile=/run/tizen-mobile-env
+Environment="CGROUP_MEMORY_SWAP=/sys/fs/cgroup/memory/swap"
+Environment="SWAPFILE=/dev/zram0"
+ExecStartPre=-/bin/mkdir -pm 755 $CGROUP_MEMORY_SWAP
+ExecStartPre=-/bin/sh -c "/bin/echo 3 > $CGROUP_MEMORY_SWAP/memory.move_charge_at_immigrate"
+ExecStart=/usr/bin/resourced -s 1 -u 20 -c swapon
+ExecStopPost=-/sbin/swapoff $SWAPFILE
+Restart=always
+RestartSec=0
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/resourced_swapoff.service b/packaging/resourced_swapoff.service
new file mode 100644 (file)
index 0000000..c07025d
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=Resource management daemon
+After=tizen-system.target
+Wants=tizen-system.target
+
+[Service]
+Type=simple
+EnvironmentFile=/run/tizen-mobile-env
+ExecStart=/usr/bin/resourced -s 1 -u 60
+Restart=always
+RestartSec=0
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/system-resource.spec b/packaging/system-resource.spec
new file mode 100644 (file)
index 0000000..f9a691b
--- /dev/null
@@ -0,0 +1,293 @@
+Name:       system-resource
+Summary:    System Resource Information
+Version:    0.2.86
+Release:    0
+VCS:        magnolia/kernel/api/system-resource#submit/master/20131010.041510-19-gcf6121a4bcce9c122d4ea05fa9974ec7bac227d8
+Group:      System/Libraries
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.gz
+Source1:    resourced.service
+Source2:    resourced-zram.service
+Source5:    resourced_swapoff.service
+Source6:    resourced-cpucgroup.service
+Source8:    resourced-logging.service
+
+%define powertop_state OFF
+%define cpu_module ON
+%define vip_agent_module ON
+%define timer_slack ON
+%define logging_module OFF
+%define logging_memory OFF
+%define logging_cpu OFF
+%define memory_module ON
+%define memory_cgroup OFF
+
+%if "%{?tizen_profile_name}" == "mobile"
+       %define swap_module OFF
+       %define network_state OFF
+       %define network_service OFF
+       %define telephony_feature OFF
+       %define tethering_feature OFF
+
+%endif
+
+%if "%{?tizen_profile_name}" == "wearable"
+       %define swap_module OFF
+       %define network_state OFF
+       %define network_service OFF
+       %define telephony_feature OFF
+
+%endif
+
+%if 0%{?tizen_build_binary_release_type_eng}
+       %define memory_eng ON
+%else
+       %define memory_eng 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
+
+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(capi-network-connection)
+BuildRequires:  pkgconfig(libsystemd-daemon)
+BuildRequires:  pkgconfig(journal)
+
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%if %{?logging_module} == ON
+BuildRequires:  pkgconfig(libsystemd-journal)
+%endif
+
+%description
+
+%package resourced
+Summary: Resource Daemon
+Group:   System/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description resourced
+Resource Daemon
+
+%package -n libresourced
+Summary: Resource Daemon Library
+Group:   System/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description -n libresourced
+Resource Daemon Library
+
+%package -n libresourced-devel
+Summary: Resource Daemon Library (Development)
+Group:   System/Libraries
+Requires:   %{name} = %{version}-%{release}
+
+%description -n libresourced-devel
+Resource Daemon Library (Development)
+
+%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
+
+%if 0%{?tizen_build_binary_release_type_eng}
+       CFLAGS+=" -DTIZEN_ENGINEER_MODE"
+%endif
+
+export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
+export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
+
+cmake . -DCMAKE_INSTALL_PREFIX=/usr -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DCMAKE_BUILD_TYPE=Release \
+       -DEXCLUDE_LIST_FILE_NAME=%{exclude_list_file_name} \
+       -DEXCLUDE_LIST_FULL_PATH=%{exclude_list_full_path} -DDATABASE_FULL_PATH=%{database_full_path} \
+       -DEXCLUDE_LIST_OPT_FULL_PATH=%{exclude_list_opt_full_path} -DNETWORK_MODULE=%{network_state} -DNETWORK_SERVICE=%{network_service} \
+       -DSWAP_MODULE=%{swap_module} -DPOWERTOP_MODULE=%{powertop_state} -DCPU_MODULE=%{cpu_module}\
+       -DMEMORY_ENG=%{memory_eng} -DVIP_AGENT=%{vip_agent_module} -DTELEPHONY_FEATURE=%{telephony_feature} \
+       -DTIMER_SLACK=%{timer_slack} -DLOGGING_MODULE=%{logging_module} -DLOGGING_MEMORY=%{logging_memory} \
+       -DLOGGING_CPU=%{logging_cpu} -DDATAUSAGE_TYPE=NFACCT -DMEMORY_MODULE=%{memory_module} -DMEMORY_CGROUP=%{memory_cgroup}  \
+       -DTETHERING_FEATURE=%{tethering_feature}
+
+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 %{?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
+%endif
+
+mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants
+%if %{?swap_module} == ON
+install -m 0644 %SOURCE1 %{buildroot}%{_libdir}/systemd/system/resourced.service
+ln -s ../resourced.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/resourced.service
+
+mkdir -p %{buildroot}%{_libdir}/systemd/system/graphical.target.wants
+install -m 0644 %SOURCE2 %{buildroot}%{_libdir}/systemd/system/resourced-zram.service
+ln -s ../resourced-zram.service %{buildroot}%{_libdir}/systemd/system/graphical.target.wants/resourced-zram.service
+%else
+install -m 0644 %SOURCE5 %{buildroot}%{_libdir}/systemd/system/resourced.service
+ln -s ../resourced.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/resourced.service
+%endif
+
+%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
+
+#powertop-wrapper part
+%if %{?powertop_state} == ON
+mkdir -p %{buildroot}/usr/share/powertop-wrapper/
+cp -p %_builddir/%name-%version/src/powertop-wrapper/header.html %{buildroot}/usr/share/powertop-wrapper
+%endif
+
+%if %{?logging_module} == ON
+#logging service file
+mkdir -p %{buildroot}%{_libdir}/systemd/system/graphical.target.wants
+install -m 0644 %SOURCE8 %{buildroot}%{_libdir}/systemd/system/resourced-logging.service
+ln -s ../resourced-logging.service %{buildroot}%{_libdir}/systemd/system/graphical.target.wants/resourced-logging.service
+%endif
+
+%pre resourced
+if [ "$1" = "2" ]; then # upgrade begins
+       systemctl stop resourced.service
+fi
+
+%post -p /sbin/ldconfig
+
+%post resourced
+
+init_vconf()
+{
+       vconftool set -t bool db/private/resourced/wifi_statistics 1 -i -f -s resourced
+       vconftool set -t bool db/private/resourced/datacall 1 -i -f -s resourced
+       vconftool set -t bool db/private/resourced/datacall_logging 1 -i -f -s resourced
+       vconftool set -t int db/private/resourced/datausage_timer 60 -i -f -s resourced
+       vconftool set -t string db/private/resourced/new_limit "" -u 5000 -f -s resourced
+       vconftool set -t string db/private/resourced/delete_limit "" -u 5000 -f -s resourced
+}
+
+%if %{?network_state} == ON
+       init_vconf
+%endif
+#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
+
+%postun -p /sbin/ldconfig
+
+%files
+/usr/share/license/%{name}
+%attr(-,root, root) %{_bindir}/resourced
+%if %{?network_state} == ON
+       %config(noreplace) %attr(660,root,app) %{database_full_path}
+       %config(noreplace) %attr(660,root,app) %{database_full_path}-journal
+       /usr/bin/datausagetool
+       %manifest resourced.manifest
+%else
+%manifest resourced_nodb.manifest
+%endif
+%config %{_sysconfdir}/dbus-1/system.d/resourced.conf
+%{_libdir}/systemd/system/resourced.service
+%{_libdir}/systemd/system/multi-user.target.wants/resourced.service
+/etc/resourced/memory.conf
+%if %{?cpu_module} == ON
+/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
+%{_libdir}/systemd/system/resourced-zram.service
+%{_libdir}/systemd/system/graphical.target.wants/resourced-zram.service
+%{_bindir}/resourced-zram.sh
+%endif
+%if %{?logging_module} == ON
+%{_libdir}/systemd/system/resourced-logging.service
+%{_libdir}/systemd/system/graphical.target.wants/resourced-logging.service
+%endif
+%if %{?vip_agent_module} == ON
+/etc/resourced/vip-process.conf
+%attr(-,root, root) %{_bindir}/vip-release-agent
+%endif
+%if %{?timer_slack} == ON
+/etc/resourced/timer-slack.conf
+%endif
+%{exclude_list_full_path}
+%if %{?powertop_state} == ON
+/usr/share/powertop-wrapper/header.html
+%endif
+
+#memps
+%attr(-,root, root) %{_bindir}/memps
+
+%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.*
+%if %{?network_state} == ON
+%{_libdir}/librd-network.so.*
+%endif
+#powertop-wrapper part
+%if %{?powertop_state} == ON
+%{_libdir}/libpowertop-wrapper.so.*
+%endif
+
+%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
+%if %{?network_state} != OFF
+%{_includedir}/system/data_usage.h
+%{_includedir}/system/rd-network.h
+%{_libdir}/librd-network.so
+/etc/resourced/network.conf
+%endif
+#powertop-wrapper part
+%if %{?powertop_state} == ON
+%{_includedir}/system/powertop-dapi.h
+%{_libdir}/libpowertop-wrapper.so
+%endif
diff --git a/resourced.conf b/resourced.conf
new file mode 100644 (file)
index 0000000..f8558b2
--- /dev/null
@@ -0,0 +1,10 @@
+<!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>
diff --git a/resourced.manifest b/resourced.manifest
new file mode 100644 (file)
index 0000000..56400ee
--- /dev/null
@@ -0,0 +1,14 @@
+<manifest>
+       <define>
+               <domain name="resourced"/>
+               <provide>
+                       <label name="resourced::db"/>
+               </provide>
+       </define>
+       <assign>
+               <filesystem path="/opt/usr/dbspace/.resourced-datausage.db*" label="resourced::db"/>
+       </assign>
+       <request>
+               <domain name="resourced"/>
+       </request>
+</manifest>
diff --git a/resourced.rule b/resourced.rule
new file mode 100644 (file)
index 0000000..d6cbe41
--- /dev/null
@@ -0,0 +1,3 @@
+resourced com.samsung.setting::private r
+resourced system::vconf_inhouse rw
+resourced system-apps r
\ No newline at end of file
diff --git a/resourced_nodb.manifest b/resourced_nodb.manifest
new file mode 100644 (file)
index 0000000..9857e46
--- /dev/null
@@ -0,0 +1,8 @@
+<manifest>
+       <define>
+               <domain name="resourced"/>
+       </define>
+       <request>
+               <domain name="resourced"/>
+       </request>
+</manifest>
diff --git a/scripts/resourced-cpucgroup.sh b/scripts/resourced-cpucgroup.sh
new file mode 100644 (file)
index 0000000..053279f
--- /dev/null
@@ -0,0 +1,15 @@
+#!/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
+
diff --git a/scripts/resourced-zram.sh b/scripts/resourced-zram.sh
new file mode 100644 (file)
index 0000000..dd35014
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+FILE="/sys/block/zram0"
+SWAP="/dev/zram0"
+RATE="20"
+
+Mem=`cat /proc/meminfo | grep "MemTotal" | awk '{print $2}'`
+
+if [ $Mem -lt 200000 ]; then
+        SIZE="8388608"
+elif [ $Mem -ge 200000 -a $Mem -lt 900000 ]; then
+        SIZE=$((Mem * RATE / 100 * 1024))
+elif [ $Mem -ge 900000 ]; then
+        SIZE="134217728"
+fi
+
+if [ ! -e $FILE ]; then
+       /sbin/modprobe zram num_devices=1
+fi
+/bin/echo $SIZE > $FILE/disksize
+/sbin/mkswap $SWAP
diff --git a/src/common/app-stat.h b/src/common/app-stat.h
new file mode 100644 (file)
index 0000000..6818fad
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  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 "data_usage.h"
+#include "daemon-options.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;
+
+       pid_t pid;
+       int ifindex;
+       resourced_roaming_type is_roaming;
+};
+
+/*
+* Structure for holding serialized data from kernel @see traffic_event
+*/
+struct traffic_stat {
+       unsigned long bytes;
+       int ifindex;
+};
+
+struct classid_iftype_key
+{
+       u_int32_t classid;
+       int iftype;
+       char ifname[MAX_NAME_LENGTH];
+};
+
+typedef GTree traffic_stat_tree;
+
+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);
+
+traffic_stat_tree *create_traffic_stat_tree(void);
+void free_traffic_stat_tree(traffic_stat_tree *list);
+
+resourced_ret_c prepare_application_stat(traffic_stat_tree *tree_in,
+                traffic_stat_tree *tree_out,
+                struct application_stat_tree *result,
+                volatile struct daemon_opts *opts);
+
+
+#endif /* _RESOURCED_APPLICATION_STAT_H_ */
diff --git a/src/common/appid-helper.c b/src/common/appid-helper.c
new file mode 100644 (file)
index 0000000..091a104
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#define BASE_NAME_PREFIX "com.samsung."
+#define DOT_DELIMETER '.'
+
+static int is_base_name(const char *appid)
+{
+       return strstr(appid, BASE_NAME_PREFIX) != 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';
+}
diff --git a/src/common/appid-helper.h b/src/common/appid-helper.h
new file mode 100644 (file)
index 0000000..3558cc2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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);
diff --git a/src/common/cgroup.c b/src/common/cgroup.c
new file mode 100644 (file)
index 0000000..b2ec884
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * 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 "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>
+
+static int 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);
+
+       strerror_r(errno, buf, sizeof(buf));
+       ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+               "Failed place all pid to cgroup %s, error %s",
+                       cgroup_full_path, 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);
+}
+
+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, 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];
+       snprintf(buf, sizeof(buf), "%s%s", cgroup_name, file_name);
+       _SD("cgroup_buf %s, value %d\n", buf, *value);
+       return fread_int(buf, value);
+}
+
+int make_cgroup_subdir(char* parentdir, char* cgroup_name, int *exists)
+{
+       int cgroup_exists = 0, ret = 0;
+       char buf[MAX_PATH_LENGTH];
+
+       ret = snprintf(buf, sizeof(buf), "%s/%s", parentdir, cgroup_name);
+       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 (exists)
+               *exists = 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);
+}
+
diff --git a/src/common/cgroup.h b/src/common/cgroup.h
new file mode 100644 (file)
index 0000000..c57bf67
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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"
+
+/**
+ * @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, char* string);
+
+/**
+ * @desc make cgroup,
+ * @param parentdir - parent cgroup path
+ * @param cgroup_name - cgroup subdirectory to write
+ * @param cgroup_exists - 1 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(char* parentdir, char* cgroup_name, int *cgroup_exists);
+
+/**
+ * @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 mount cgroup,
+ * @param source -cgroup name
+ * @param mount_point - cgroup path
+ * @param opts - mount options
+ * @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);
+
+#endif /*_CGROUP_LIBRARY_CGROUP_H_*/
diff --git a/src/common/config-parser.c b/src/common/config-parser.c
new file mode 100644 (file)
index 0000000..e184a5b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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 "trace.h"
+#include "config-parser.h"
+
+#define MAX_LINE       128
+#define MAX_SECTION    64
+#define WHITESPACE     " \t"
+#define NEWLINE                "\n\r"
+#define COMMENT                '#'
+
+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[MAX_LINE];
+       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, MAX_LINE, 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));
+                       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;
+}
+
diff --git a/src/common/config-parser.h b/src/common/config-parser.h
new file mode 100644 (file)
index 0000000..f277fa9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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__
+
+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);
+
+#endif
+
diff --git a/src/common/config.h.in b/src/common/config.h.in
new file mode 100644 (file)
index 0000000..a25e902
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _CONFIG_H_GENERATED
+#define _CONFIG_H_GENERATED
+
+#cmakedefine DEBUG_ENABLED  @DEBUG_ENABLED@
+#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*/
diff --git a/src/common/const.h b/src/common/const.h
new file mode 100644 (file)
index 0000000..4c94396
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  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
+ *
+ *  @desc Application stat entity
+ *  @version 1.0
+ *
+ */
+
+#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 COMMA_DELIMETER ","
+
+#define COUNTER_UPDATE_PERIOD 60
+#define FLUSH_PERIOD 60
+#define STORE_DELAY_INTERVAL 1
+
+#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_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,
+};
+
+#endif /* _RESOURCED_CONST_H */
diff --git a/src/common/daemon-options.h b/src/common/daemon-options.h
new file mode 100644 (file)
index 0000000..a046dd6
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2013 - 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.h
+ *
+ *  @desc Entity for working with daemon options
+ *
+ */
+
+#ifndef _RESOURCED_DATAUSAGE_DAEMON_OPTIONS_H
+#define _RESOURCED_DATAUSAGE_DAEMON_OPTIONS_H
+
+
+#include <sys/types.h>
+#include <signal.h>
+
+struct daemon_opts {
+       sig_atomic_t is_update_quota;
+       sig_atomic_t datacall_logging;  /**< type of rsml_datacall_logging_option */
+       sig_atomic_t start_daemon;
+       sig_atomic_t update_period;
+       sig_atomic_t flush_period;
+       sig_atomic_t state;
+       sig_atomic_t enable_swap;
+};
+
+/* TODO remove */
+void load_daemon_opts(struct daemon_opts *daemon_options);
+
+#endif /* _RESOURCED_DATAUSAGE_DAEMON_OPTIONS_H */
diff --git a/src/common/edbus-handler.c b/src/common/edbus-handler.c
new file mode 100644 (file)
index 0000000..1a89ca2
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * 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 "trace.h"
+#include "edbus-handler.h"
+#include "macro.h"
+#include "resourced.h"
+
+#define EDBUS_INIT_RETRY_COUNT 5
+
+struct edbus_list{
+       char *signal_name;
+       E_DBus_Signal_Handler *handler;
+};
+
+static struct edbus_object edbus_objects[] = {
+       { RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP   , NULL, NULL },
+       { RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM, NULL, NULL },
+       { RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, NULL, NULL },
+       { RESOURCED_PATH_NETWORK, RESOURCED_INTERFACE_NETWORK, 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 '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, &param[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, &param[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);
+               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);
+               return NULL;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg,
+                       DBUS_REPLY_TIMEOUT, &err);
+       if (!reply) {
+               _E("dbus_connection_send error(No reply)");
+       }
+
+       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);
+       return reply;
+}
+
+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);
+               }
+       }
+}
+
+int register_edbus_signal_handler(const char *path, const char *interface,
+               const char *name, E_DBus_Signal_Cb cb)
+{
+       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)
+                       return RESOURCED_ERROR_FAIL;
+       }
+
+       handler = e_dbus_signal_handler_add(edbus_conn, NULL, path,
+                               interface, name, cb, NULL);
+
+       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");
+               free(entry);
+               return -1;
+       }
+
+       entry->handler = handler;
+       edbus_handler_list = eina_list_prepend(edbus_handler_list, entry);
+       if (!edbus_handler_list) {
+               _E("eina_list_prepend failed");
+               free(entry->signal_name);
+               free(entry);
+               return RESOURCED_ERROR_FAIL;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+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++) {
+               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;
+}
+
+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);
+}
+
+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, 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();
+}
diff --git a/src/common/edbus-handler.h b/src/common/edbus-handler.h
new file mode 100644 (file)
index 0000000..e620521
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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;
+};
+
+#define DBUS_REPLY_TIMEOUT  (120 * 1000)
+
+#define BUS_NAME               "org.tizen.resourced"
+#define OBJECT_PATH            "/Org/Tizen/ResourceD"
+#define INTERFACE_NAME         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            OBJECT_PATH"/Swap"
+#define RESOURCED_INTERFACE_SWAP       INTERFACE_NAME".swap"
+
+#define RESOURCED_PATH_OOM             OBJECT_PATH"/Oom"
+#define RESOURCED_INTERFACE_OOM                INTERFACE_NAME".oom"
+
+#define RESOURCED_PATH_NETWORK         OBJECT_PATH"/Network"
+#define RESOURCED_INTERFACE_NETWORK    INTERFACE_NAME".network"
+
+#define RESOURCED_PATH_PROCESS         OBJECT_PATH"/Process"
+#define RESOURCED_INTERFACE_PROCESS    INTERFACE_NAME".process"
+
+/*
+ * Logging
+ */
+#define RESOURCED_PATH_LOGGING         OBJECT_PATH"/Logging"
+#define RESOURCED_INTERFACE_LOGGING    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_WATCHDOG SYSTEM_POPUP_PATH_NAME"/System"
+#define SYSTEM_POPUP_IFACE_WATCHDOG SYSTEM_POPUP_BUS_NAME".System"
+
+/*
+ * Deviced
+ */
+#define DEVICED_BUS_NAME               "org.tizen.system.deviced"
+#define DEVICED_PATH_PROCESS           "/Org/Tizen/System/DeviceD/Process"
+#define DEVICED_INTERFACE_PROCESS      DEVICED_BUS_NAME".Process"
+
+#define DEVICED_PATH_DISPLAY               "/Org/Tizen/System/DeviceD/Display"
+#define DEVICED_INTERFACE_DISPLAY      DEVICED_BUS_NAME".display"
+
+#define SIGNAL_LCD_ON  "LCDOn"
+#define SIGNAL_LCD_OFF "LCDOff"
+
+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);
+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);
+int register_edbus_interface(struct edbus_object *object);
+
+void edbus_init(void);
+void edbus_exit(void);
+
+#endif /* __EDBUS_HANDLE_H__ */
diff --git a/src/common/file-helper.c b/src/common/file-helper.c
new file mode 100644 (file)
index 0000000..e52db15
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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"
+
+#define BUF_MAX         (BUFSIZ)
+#define BUF_INC_SIZE    (512 * 1024)
+#define KB(bytes)       ((bytes)/1024)
+
+resourced_ret_c fwrite_str(const char *path, const char *str)
+{
+       FILE *f;
+       int ret;
+
+       ret_value_msg_if(!path, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "please provide valid name file!\n");
+       ret_value_msg_if(!str, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "please provide valid string!\n");
+
+       f = fopen(path, "w");
+       ret_value_errno_msg_if(!f, RESOURCED_ERROR_FAIL, "Fail to file open");
+
+       ret = fputs(str, f);
+       fclose(f);
+       ret_value_errno_msg_if(ret == EOF, RESOURCED_ERROR_FAIL,
+                              "Fail to write file\n");
+
+       return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c fwrite_int(const char *path, const int number)
+{
+       char digit_buf[MAX_DEC_SIZE(int)];
+       int ret;
+
+       ret = snprintf(digit_buf, sizeof(digit_buf), "%d", number);
+       ret_value_errno_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+                              "snprintf failed\n");
+
+       return fwrite_str(path, digit_buf);
+}
+
+resourced_ret_c fwrite_uint(const char *path, const u_int32_t number)
+{
+       char digit_buf[MAX_DEC_SIZE(u_int32_t)];
+       int ret;
+
+       ret = snprintf(digit_buf, sizeof(digit_buf), "%u", number);
+       ret_value_errno_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+                              "snprintf failed\n");
+
+       return fwrite_str(path, digit_buf);
+}
+
+resourced_ret_c fread_int(const char *path, u_int32_t *number)
+{
+       FILE *f;
+       int ret;
+
+       f = fopen(path, "r");
+
+       ret_value_errno_msg_if(!f, RESOURCED_ERROR_FAIL, "Fail to open file");
+
+       ret = fscanf(f, "%u", number);
+       fclose(f);
+       ret_value_errno_msg_if(ret == EOF, RESOURCED_ERROR_FAIL,
+                              "Fail to read file\n");
+
+       return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c fwrite_array(const char *path, const void *array,
+                            const size_t size_of_elem,
+                            const size_t numb_of_elem)
+{
+       FILE *f;
+       int ret;
+
+       ret_value_msg_if(!array, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "please provide valid array of elements!\n");
+
+       f = fopen(path, "w");
+
+       ret_value_errno_msg_if(!f, RESOURCED_ERROR_FAIL,
+                              "Failed open %s file\n", path);
+
+       ret = fwrite(array, size_of_elem, numb_of_elem, f);
+       fclose(f);
+       ret_value_errno_msg_if(ret != numb_of_elem, RESOURCED_ERROR_FAIL,
+                              "Failed write array into %s file\n");
+
+       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;
+       int     fd  = open(path, O_RDONLY);
+
+       if (fd < 0) {
+               _E("%s open error", 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;
+               }
+       } while (ret > 0);
+       close(fd);
+
+       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;
+}
diff --git a/src/common/file-helper.h b/src/common/file-helper.h
new file mode 100644 (file)
index 0000000..cbee9e3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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
+ */
+resourced_ret_c fwrite_str(const char *path, const char *str);
+
+resourced_ret_c fwrite_int(const char *path, const int number);
+
+resourced_ret_c fwrite_uint(const char *path, const u_int32_t number);
+
+resourced_ret_c fread_int(const char *path, u_int32_t *number);
+
+resourced_ret_c 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);
+#endif  /*_RESOURCED_FILE_HELPER_H_*/
diff --git a/src/common/genl.h b/src/common/genl.h
new file mode 100644 (file)
index 0000000..2511733
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  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_ */
diff --git a/src/common/logging-common.h b/src/common/logging-common.h
new file mode 100644 (file)
index 0000000..9923896
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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-common.h
+ * @desc logging common process
+ **/
+
+#ifndef __LOGGING_COMMON_H__
+#define __LOGGING_COMMON_H__
+
+enum logging_control_type {
+       LOGGING_INSERT_PROC_LIST,
+       LOGGING_UPDATE_PROC_INFO,
+       LOGGING_UPDATE_STATE
+};
+
+struct logging_data_type {
+       enum logging_control_type control_type;
+       unsigned long *args;
+};
+
+#ifdef LOGGING_SUPPORT
+int logging_control(enum logging_control_type type, unsigned long *args);
+#else
+static inline int logging_control(enum logging_control_type type, unsigned long *args)
+{
+       return RESOURCED_ERROR_NONE;
+}
+#endif /* LOGGING_SUPPORT */
+
+#endif /* __LOGGING_COMMON_H__ */
diff --git a/src/common/lowmem-common.h b/src/common/lowmem-common.h
new file mode 100644 (file)
index 0000000..caaa7a6
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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;
+       unsigned long *args;
+};
+
+#ifdef MEMORY_SUPPORT
+extern int lowmem_control(enum lowmem_control_type type, unsigned long *args);
+#else
+static inline int lowmem_control(enum lowmem_control_type type, unsigned long *args)
+{
+       return RESOURCED_ERROR_NONE;
+}
+#endif /* MEMORY_SUPPORT */
+
+#endif /* __LOWMEM_COMMON_H__ */
diff --git a/src/common/macro.h b/src/common/macro.h
new file mode 100644 (file)
index 0000000..f47111c
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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>
+
+#define API __attribute__((visibility("default")))
+
+#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 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 { \
+               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_list_next(elem), node = NULL; \
+                       elem && ((node = elem->data) != NULL); \
+                       elem = elem_next, elem_next = g_list_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_ */
diff --git a/src/common/module-data.c b/src/common/module-data.c
new file mode 100644 (file)
index 0000000..ce81e5f
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 modules_arg *marg, struct daemon_arg *darg)
+{
+       ret_msg_if(marg == NULL || darg == NULL,
+                        "Init modules argument failed\n");
+       marg->opts = darg->opts;
+}
diff --git a/src/common/module-data.h b/src/common/module-data.h
new file mode 100644 (file)
index 0000000..37f8e9b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 "daemon-options.h"
+#include "init.h"
+#include "proc-main.h"
+
+struct modules_arg {
+       struct daemon_opts *opts;
+};
+
+struct swap_module_data {
+       int swaptype;                   /* swap */
+};
+
+struct shared_modules_data {
+       struct counter_arg *carg;
+       struct swap_module_data swap_data;
+};
+
+struct shared_modules_data *get_shared_modules_data(void);
+
+void init_modules_arg(struct modules_arg *marg, struct daemon_arg *darg);
+
+#endif /* __MODULE_DATA_HANDLE_H__ */
diff --git a/src/common/module.c b/src/common/module.c
new file mode 100644 (file)
index 0000000..33734bb
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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 <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 *data)
+{
+       GSList *iter;
+       const struct module_ops *module;
+       int ret_code = RESOURCED_ERROR_NONE;
+
+       gslist_for_each_item(iter, modules_list) {
+               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;
+               }
+       }
+}
+
+void modules_init(void *data)
+{
+       GSList *iter;
+       const struct module_ops *module;
+       int ret_code = RESOURCED_ERROR_NONE;
+
+       gslist_for_each_item(iter, modules_list) {
+               module = (struct module_ops *)iter->data;
+               _D("Initialize [%s] module\n", module->name);
+               if (module->init)
+                       ret_code = module->init(data);
+               if (ret_code < 0)
+                       _E("Fail to initialize [%s] module\n", module->name);
+       }
+}
+
+void modules_exit(void *data)
+{
+       GSList *iter;
+       /* Deinitialize in reverse order */
+       GSList *reverse_list = g_slist_reverse(modules_list);
+       const struct module_ops *module;
+       int ret_code = RESOURCED_ERROR_NONE;
+
+       gslist_for_each_item(iter, reverse_list) {
+               module = (struct module_ops *)iter->data;
+               _D("Deinitialize [%s] module\n", module->name);
+               if (module->exit)
+                       ret_code = module->exit(data);
+               if (ret_code < 0)
+                       _E("Fail to deinitialize [%s] module\n", module->name);
+       }
+}
diff --git a/src/common/module.h b/src/common/module.h
new file mode 100644 (file)
index 0000000..7387cd5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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,
+};
+
+struct module_ops {
+       enum module_priority priority;
+       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);
+};
+
+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_exit(void *data);
+
+const struct module_ops *find_module(const char *name);
+
+#endif /* __MODULE_HANDLE_H__ */
diff --git a/src/common/notifier.c b/src/common/notifier.c
new file mode 100644 (file)
index 0000000..f994d44
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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(&notifier_ops)
+
diff --git a/src/common/notifier.h b/src/common/notifier.h
new file mode 100644 (file)
index 0000000..46f112a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 {
+       RESOURCED_NOTIFIER_APP_LAUNCH,
+       RESOURCED_NOTIFIER_APP_RESUME,
+       RESOURCED_NOTIFIER_APP_FOREGRD,
+       RESOURCED_NOTIFIER_APP_BACKGRD,
+       RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+       RESOURCED_NOTIFIER_APP_ACTIVE,
+       RESOURCED_NOTIFIER_APP_INACTIVE,
+       RESOURCED_NOTIFIER_APP_TERMINATE,
+       RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID,
+       RESOURCED_NOTIFIER_SWAP_START,
+       RESOURCED_NOTIFIER_SWAP_RESTART,
+       RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP,
+       RESOURCED_NOTIFIER_LCD_ON,
+       RESOURCED_NOTIFIER_LCD_OFF,
+       RESOURCED_NOTIFIER_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__ */
diff --git a/src/common/swap-common.h b/src/common/swap-common.h
new file mode 100644 (file)
index 0000000..7760e5c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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__
+
+enum swap_status_type {
+       SWAP_GET_TYPE,
+       SWAP_GET_CANDIDATE_PID,
+       SWAP_GET_STATUS,
+       SWAP_CHECK_PID,
+       SWAP_CHECK_CGROUP,
+};
+
+enum {
+       SWAP_OFF,
+       SWAP_ON,
+       SWAP_ARG_END,
+};
+
+enum {
+       SWAP_FALSE,
+       SWAP_TRUE,
+};
+
+#ifdef SWAP_SUPPORT
+extern int swap_status(enum swap_status_type type, unsigned long *args);
+#else
+static inline int swap_status(enum swap_status_type type, unsigned long *args)
+{
+       return RESOURCED_ERROR_NONE;
+}
+#endif /* SWAP_SUPPORT */
+
+#endif /* __SWAP_COMMON_H__ */
diff --git a/src/common/trace.h b/src/common/trace.h
new file mode 100644 (file)
index 0000000..6e17947
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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>
+
+#undef LOG_TAG
+#define LOG_TAG "RESOURCED"
+
+#define WALK_TREE(list, func) g_tree_foreach((GTree *)list, func, NULL)
+
+#define _E(fmt, arg...) LOGE("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _D(fmt, arg...) LOGD("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _I(fmt, arg...) LOGI("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+
+#define _SE(fmt, arg...) SECURE_LOGE("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _SD(fmt, arg...) SECURE_LOGD("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _SI(fmt, arg...) SECURE_LOGI("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##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]; \
+       strerror_r(-error_code, buf, sizeof(buf)); \
+       _##type("errno %d, errmsg %s", error_code, 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); \
+       strerror_r(-error_code, buf, sizeof(buf)); \
+       _##type("errno %d, errmsg %s", error_code, 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)
+
+#endif /* _SYSTEM_RESOURCE_TRACE_H_ */
diff --git a/src/common/transmission.h b/src/common/transmission.h
new file mode 100644 (file)
index 0000000..48c8fa9
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * @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 */
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
new file mode 100644 (file)
index 0000000..db677ea
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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 "notifier.h"
+#include "proc-main.h"
+#include "proc-process.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"
+
+#define CPU_DEFAULT_CGROUP "/sys/fs/cgroup/cpu"
+#define CPU_CONTROL_GROUP "/sys/fs/cgroup/cpu/background"
+#define CPU_CONTROL_SERVICE_GROUP "/sys/fs/cgroup/cpu/service"
+#define CPU_CONF_FILE                  "/etc/resourced/cpu.conf"
+#define CPU_CONF_SECTION       "CONTROL"
+#define CPU_CONF_PREDEFINE     "PREDEFINE"
+#define CPU_SHARE      "/cpu.shares"
+
+static int cpu_move_cgroup(pid_t pid, char *path)
+{
+       return cgroup_write_node(path, CGROUP_FILE_NAME, pid);
+}
+
+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);
+       } else if (!strcmp(result->name, "BACKGROUND_CPU_SHARE")) {
+               value = atoi(result->value);
+               if (value)
+                       cgroup_write_node(CPU_CONTROL_GROUP, CPU_SHARE, value);
+       } else if (!strcmp(result->name, "SERVICE_CPU_SHARE")) {
+               value = atoi(result->value);
+               if (value)
+                       cgroup_write_node(CPU_CONTROL_SERVICE_GROUP, CPU_SHARE, value);
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_service_launch(void *data)
+{
+       struct proc_status *p_data = (struct proc_status*)data;
+       _D("cpu_service_launch : pid = %d, appname = %s", p_data->pid, p_data->appid);
+       cpu_move_cgroup(p_data->pid, CPU_CONTROL_SERVICE_GROUP);
+       return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_foreground_state(void *data)
+{
+       struct proc_status *p_data = (struct proc_status*)data;
+       _D("cpu_foreground_state : pid = %d, appname = %s", p_data->pid, p_data->appid);
+       cpu_move_cgroup(p_data->pid, CPU_DEFAULT_CGROUP);
+       return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_background_state(void *data)
+{
+       struct proc_status *p_data = (struct proc_status*)data;
+       _D("cpu_background_state : pid = %d, appname = %s", p_data->pid, p_data->appid);
+       cpu_move_cgroup(p_data->pid, CPU_CONTROL_SERVICE_GROUP);
+       return RESOURCED_ERROR_NONE;
+}
+
+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_DEFAULT_CGROUP, "service", NULL);
+       ret_value_msg_if(ret_code < 0, ret_code, "create service cgroup failed\n");
+       config_parse(CPU_CONF_FILE, load_cpu_config, NULL);
+
+       register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_service_launch);
+       register_notifier(RESOURCED_NOTIFIER_APP_RESUME, cpu_foreground_state);
+       register_notifier(RESOURCED_NOTIFIER_APP_FOREGRD, cpu_foreground_state);
+       register_notifier(RESOURCED_NOTIFIER_APP_BACKGRD, cpu_background_state);
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_cpu_finalize(void *data)
+{
+       unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, cpu_service_launch);
+       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);
+       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)
diff --git a/src/cpu/cpu.conf b/src/cpu/cpu.conf
new file mode 100644 (file)
index 0000000..609c464
--- /dev/null
@@ -0,0 +1,6 @@
+[CONTROL]
+# predefined process list
+PREDEFINE=indicator
+PREDEFINE=net-config
+BACKGROUND_CPU_SHARE=50
+SERVICE_CPU_SHARE=128
diff --git a/src/cpu/logging-cpu.c b/src/cpu/logging-cpu.c
new file mode 100644 (file)
index 0000000..c345016
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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 logging-cpu.c
+ *
+ * @desc start cpu logging system for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <linux/limits.h>
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include <dirent.h>
+#include <sys/utsname.h>
+#include <systemd/sd-journal.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "module.h"
+#include "macro.h"
+#include "proc-process.h"
+#include "logging.h"
+
+#define        PROC_STAT_PATH "/proc/%d/stat"
+#define        CPU_NAME "cpu"
+#define        CPU_COMMIT_INTERVAL             30*60   /* 20 min */
+
+#define        CPU_MAX_INTERVAL                20*60   /* 5 min */
+#define        CPU_INIT_INTERVAL               20*60   /* 3 min */
+#define        CPU_FOREGRD_INTERVAL            3*60    /* 1 min */
+#define        CPU_BACKGRD_INTERVAL            10*60   /* 2 min */
+#define        CPU_BACKGRD_OLD_INTERVAL        15*60   /* 5 min */
+
+struct logging_cpu_info {
+       unsigned long utime;
+       unsigned long stime;
+       unsigned long last_utime;
+       unsigned long last_stime;
+       bool last_commited;
+       time_t last_log_time;
+       time_t log_interval;
+       pid_t last_pid;
+};
+
+static int 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 void update_log_interval(struct logging_cpu_info *loginfo, int oom,
+       int always)
+{
+       if (!always && (oom < OOMADJ_FOREGRD_LOCKED))
+               return;
+
+       switch (oom) {
+       case OOMADJ_DISABLE:
+       case OOMADJ_SERVICE_MIN:
+       case OOMADJ_SU:
+               loginfo->log_interval = CPU_MAX_INTERVAL;
+               break;
+       case OOMADJ_INIT:
+               loginfo->log_interval = CPU_INIT_INTERVAL;
+               break;
+       case OOMADJ_FOREGRD_LOCKED:
+       case OOMADJ_FOREGRD_UNLOCKED:
+       case OOMADJ_BACKGRD_LOCKED:
+       case OOMADJ_SERVICE_DEFAULT:
+       case OOMADJ_SERVICE_FOREGRD:
+               loginfo->log_interval = CPU_FOREGRD_INTERVAL;
+               break;
+       case OOMADJ_BACKGRD_UNLOCKED:
+       case OOMADJ_SERVICE_BACKGRD:
+               loginfo->log_interval = CPU_BACKGRD_INTERVAL;
+               break;
+       default:
+               if (oom > OOMADJ_BACKGRD_UNLOCKED)
+                       loginfo->log_interval = CPU_BACKGRD_OLD_INTERVAL;
+               break;
+       }
+}
+
+static int init_cpu_info(void **pl, int pid, int oom, time_t now)
+{
+       struct logging_cpu_info *info;
+
+       info = (struct logging_cpu_info *)
+                       malloc(sizeof(struct logging_cpu_info));
+       if (!info) {
+               _E("malloc for logging_cpu_info is failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+       info->last_pid = pid;
+       info->utime = 0;
+       info->stime = 0;
+       info->last_utime = 0;
+       info->last_stime = 0;
+       info->last_log_time = now;
+       info->last_commited = false;
+
+       update_log_interval(info, oom, 1);
+       *pl = (void *)info;
+       return RESOURCED_ERROR_NONE;
+}
+
+/* pss_interval should be adjusted depending on app type */
+static int update_cpu_info(void *pl, pid_t pid, int oom,
+       time_t now, unsigned always)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct logging_cpu_info *loginfo = (struct logging_cpu_info *)pl;
+       unsigned long utime = 0, stime = 0;
+       unsigned long utime_diff = 0, stime_diff = 0;
+
+       if (!always) {
+               unsigned long update_interval = now - loginfo->last_log_time;
+               if (update_interval < CPU_MAX_INTERVAL) {
+                       if (now < loginfo->last_log_time + loginfo->log_interval)
+                               return ret;
+               } else {
+                       loginfo->last_log_time = now;
+               }
+       }
+
+       ret = get_cpu_time(pid, &utime, &stime);
+
+       if (ret != RESOURCED_ERROR_NONE)
+               return ret;
+
+       if (loginfo->last_pid == pid) {
+               if (loginfo->last_utime > utime)
+                       goto out;
+
+               utime_diff = utime - loginfo->last_utime;
+               loginfo->last_utime = utime;
+               loginfo->utime += utime_diff;
+               if (loginfo->stime > stime)
+                       goto out;
+               stime_diff = stime - loginfo->last_stime;
+               loginfo->last_stime = stime;
+               loginfo->stime += stime_diff;
+
+       } else {
+               loginfo->last_pid = pid;
+               loginfo->utime += utime;
+               loginfo->stime += stime;
+               loginfo->last_utime = utime;
+               loginfo->last_stime = stime;
+       }
+
+out:
+       loginfo->last_log_time = now;
+       loginfo->last_commited = false;
+       update_log_interval(loginfo, oom, 0);
+       return ret;
+}
+
+static int write_cpu_info(char *name, struct logging_infos *infos,
+       int ss_index)
+{
+       struct logging_cpu_info *ci = infos->stats[ss_index];
+
+       if (!infos->running && ci->last_commited)
+               return RESOURCED_ERROR_NONE;
+
+       sd_journal_send("NAME=cpu",
+               "TIME=%ld", ci->last_log_time,
+               "PNAME=%s", name,
+               "UTIME=%ld", ci->utime,
+               "STIME=%ld", ci->stime,
+               NULL);
+
+       ci->last_commited = true;
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static struct logging_info_ops cpu_info_ops = {
+       .update = update_cpu_info,
+       .write  = write_cpu_info,
+       .init   = init_cpu_info,
+};
+
+static int logging_cpu_init(void *data)
+{
+       int ret;
+
+       ret = register_logging_subsystem(CPU_NAME, &cpu_info_ops);
+       if(ret != RESOURCED_ERROR_NONE) {
+               _E("register logging subsystem failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+       ret = update_commit_interval(CPU_NAME, CPU_COMMIT_INTERVAL);
+       if(ret != RESOURCED_ERROR_NONE) {
+               _E("update commit interval logging subsystem failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       _D("logging cpu init finished");
+       return RESOURCED_ERROR_NONE;
+}
+
+static int logging_cpu_exit(void *data)
+{
+       _D("logging cpu exit");
+       return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops logging_cpu_ops = {
+       .priority       = MODULE_PRIORITY_NORMAL,
+       .name           = "logging_cpu",
+       .init           = logging_cpu_init,
+       .exit           = logging_cpu_exit,
+};
+
+MODULE_REGISTER(&logging_cpu_ops)
diff --git a/src/logging/include/logging.h b/src/logging/include/logging.h
new file mode 100644 (file)
index 0000000..e64a223
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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__
+
+#define        SS_NAME_MAX 10
+
+struct logging_infos {
+       pid_t pid;
+       int oom;
+       void **stats;
+       bool running;
+};
+
+struct logging_info_ops {
+       int (*update)(void *, pid_t, int, time_t, unsigned);
+       int (*write)(char *, struct logging_infos *, int);
+       int (*init)(void **, pid_t, int, time_t);
+};
+
+int register_logging_subsystem(const char *name, struct logging_info_ops *ops);
+int update_commit_interval(const char *name, time_t commit_interval);
+int get_pss(pid_t pid, unsigned *pss, unsigned *uss);
+#endif /*__LOGGING_H__*/
diff --git a/src/logging/logging.c b/src/logging/logging.c
new file mode 100644 (file)
index 0000000..8f1a97e
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * 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 <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/sysinfo.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <Ecore.h>
+
+#include "trace.h"
+#include "logging-common.h"
+#include "resourced.h"
+#include "macro.h"
+#include "module.h"
+#include "proc-process.h"
+#include "proc-main.h"
+#include "proc_stat.h"
+#include "logging.h"
+#include "edbus-handler.h"
+
+#define        BUF_MAX                 1024
+#define        WRITE_INFO_MAX          10
+#define        MAX_PROC_LIST           200
+
+#define        WEBPROCESS_NAME         "WebProcess"
+#define        MAX_PROC_ITEM           200
+#define        INC_PROC_ITEM           10
+#define        COMMIT_INTERVAL         10*60   /* 10 min */
+#define LOGGING_PTIORITY       -20
+
+#define SIGNAL_LOGGING_INIT    "LoggingInit"
+#define SIGNAL_LOGGING_GET     "LoggingGet"
+#define SIGNAL_LOGGING_UPDATED "LoggingUpdated"
+#define PROC_OOM_SCORE_ADJ_PATH        "/proc/%d/oom_score_adj"
+
+struct logging_sub_sys {
+       const char *name;
+       time_t commit_interval;
+       time_t last_commit;
+       struct logging_info_ops *ops;
+};
+
+static const struct module_ops logging_modules_ops;
+static const struct module_ops *logging_ops;
+
+static int num_log_infos;
+static bool need_to_update;
+static GHashTable *logging_proc_list;
+static GArray *logging_ss_list;
+static pthread_t       logging_thread  = 0;
+static pthread_mutex_t logging_mutex   = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t proc_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  logging_cond    = PTHREAD_COND_INITIALIZER;
+
+static int init_logging_infos(struct logging_infos *info, const char *key,
+       pid_t pid, int oom, time_t now)
+{
+       int i;
+       int ret;
+
+       if (!info) {
+               _D("info is null");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       info->oom = oom;
+       info->pid = pid;
+       info->running = true;
+
+       for (i = 0; i < logging_ss_list->len; i++) {
+               struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
+                                               struct logging_sub_sys, i);
+               ret = ss->ops->init(&(info->stats[i]), pid, oom, now);
+               if (ret != RESOURCED_ERROR_NONE) {
+                       _E("init logging at %lu", now);
+                       /* not return error, just continue updating */
+               }
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static void update_logging_infos(struct logging_infos *info,
+       time_t now, unsigned first)
+{
+       int i;
+       int ret;
+       for (i = 0; i < logging_ss_list->len; i++) {
+               struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
+                                               struct logging_sub_sys, i);
+               ret = ss->ops->update(info->stats[i], info->pid, info->oom, now, first);
+               if (ret != RESOURCED_ERROR_NONE) {
+                       /*
+                        * when update failed, this is because there is no
+                        * running process. So, just update processes running
+                        * state.
+                        */
+                       info->running = false;
+               }
+       }
+
+       return;
+}
+
+static void insert_hash_table(char *key, pid_t pid, int oom)
+{
+       struct logging_infos *info;
+       void **stats;
+       char *name;
+       time_t now;
+
+       if (!key) {
+               _D("input parameter key is NULL");
+               return;
+       }
+
+       now = time(NULL);
+
+       name = strndup(key, strlen(key)+1);
+
+       if (!name) {
+               _D("memory allocation for name failed");
+               return;
+       }
+       info = (struct logging_infos *)malloc(sizeof(struct logging_infos));
+
+       if (!info) {
+               _D("memory allocation for logging_infos failed");
+               free(name);
+               return;
+       }
+
+       stats = (void **)malloc(sizeof(void *) * num_log_infos);
+
+       if (!stats) {
+               _D("memory allocation for log infos fails");
+               free(name);
+               free(info);
+               return;
+       }
+
+       info->stats = stats;
+       init_logging_infos(info, name, pid, oom, now);
+
+       g_hash_table_insert(logging_proc_list, (gpointer) name, (gpointer) info);
+       update_logging_infos(info, now, true);
+       return;
+}
+
+static int write_journal(struct logging_sub_sys *pss, int ss_index)
+{
+       gpointer value;
+       gpointer key;
+       int ret = RESOURCED_ERROR_NONE;
+       char *name;
+       GHashTableIter iter;
+       struct logging_infos *infos;
+       g_hash_table_iter_init(&iter, logging_proc_list);
+
+       while (1) {
+               ret = pthread_mutex_lock(&proc_list_mutex);
+               if (ret) {
+                       _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
+                       return ret;
+               }
+
+               if (!g_hash_table_iter_next(&iter, &key, &value)) {
+                       ret = pthread_mutex_unlock(&proc_list_mutex);
+                       if (ret) {
+                               _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+                               return ret;
+                       }
+                       break;
+               }
+
+               name = (char *)key;
+               infos = (struct logging_infos *)value;
+               pss->ops->write(name, infos, ss_index);
+               ret = pthread_mutex_unlock(&proc_list_mutex);
+               if (ret) {
+                       _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+                       return ret;
+               }
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int write_logging_subsys_info(struct logging_sub_sys *pss, int sindex,
+       time_t now, bool force)
+{
+
+       if (!force && now < pss->last_commit + pss->commit_interval)
+               return RESOURCED_ERROR_NONE;
+
+       _D("start write %s subsys, now %lu, last:%lu, interval:%lu",
+               pss->name, now, pss->last_commit, pss->commit_interval);
+
+       write_journal(pss, sindex);
+
+       pss->last_commit = now;
+       return RESOURCED_ERROR_NONE;
+
+}
+
+static int write_logging_infos(bool force)
+{
+       int i;
+       int ret;
+       time_t now;
+
+       now = time(NULL);
+
+       for (i = 0; i < logging_ss_list->len; i++) {
+               struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
+                                               struct logging_sub_sys, i);
+               ret = write_logging_subsys_info(ss, i, now, force);
+               if (ret != RESOURCED_ERROR_NONE) {
+                       _E("write logging at %lu", now);
+                       /* not return error, just continue updating */
+               }
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+int register_logging_subsystem(const char*name, struct logging_info_ops *ops)
+{
+       struct logging_sub_sys ss;
+       char *ss_name;
+       time_t now;
+
+       if (!name) {
+               _E("input parameter name is NULL");
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+
+       ss_name = strndup(name, strlen(name)+1);
+
+       if (!ss_name) {
+               _E("memory allocation for name is failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       now = time(NULL);
+
+       ss.name = ss_name;
+       ss.commit_interval = COMMIT_INTERVAL;
+       ss.last_commit = now;
+       ss.ops = ops;
+
+       g_array_append_val(logging_ss_list, ss);
+       num_log_infos++;
+
+       return RESOURCED_ERROR_NONE;
+}
+
+int update_commit_interval(const char *name, time_t commit_interval)
+{
+       int i;
+       for (i = 0; i < logging_ss_list->len; i++) {
+               struct logging_sub_sys *ss = &g_array_index(logging_ss_list,
+                                               struct logging_sub_sys, i);
+               if (!strcmp(ss->name, name)) {
+                       ss->commit_interval = commit_interval;
+                       _D("%s logging subsystem commit interval updated to %lu",
+                       ss->name, ss->commit_interval);
+                       return RESOURCED_ERROR_NONE;
+               }
+       }
+
+       _D("%s subsystem update fail, not exist", name);
+       return RESOURCED_ERROR_FAIL;
+}
+
+static inline int is_webprocess(char *name)
+{
+       return !strcmp(name, WEBPROCESS_NAME);
+}
+
+static int rename_webprocess(pid_t pgid, char *name)
+{
+       char webui_name[PROC_NAME_MAX];
+       int ret;
+
+       if ((ret = proc_get_cmdline(pgid, webui_name)) != RESOURCED_ERROR_NONE)
+               return RESOURCED_ERROR_FAIL;
+
+       strcat(name, ".");
+       strcat(name, webui_name);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int get_cmdline(pid_t pid, char *cmdline)
+{
+       char buf[PROC_BUF_MAX];
+       FILE *fp;
+
+       snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+       fp = fopen(buf, "r");
+       if (fp == NULL)
+               return RESOURCED_ERROR_FAIL;
+
+       if (fgets(cmdline, PROC_NAME_MAX-1, fp) == NULL) {
+               fclose(fp);
+               return RESOURCED_ERROR_FAIL;
+       }
+       fclose(fp);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static void insert_proc_list(pid_t pid, pid_t pgid, int oom)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       char name[PROC_NAME_MAX];
+       struct logging_infos *info;
+
+       ret = get_cmdline(pid, name);
+       /*
+        * if cmdline does not exist, remove item from queue
+        * and continue logging remaining items
+        */
+       if (ret != RESOURCED_ERROR_NONE) {
+               return;
+       }
+
+       if (is_webprocess(name)) {
+               ret = rename_webprocess(pgid, name);
+               if (ret != RESOURCED_ERROR_NONE)
+                       return;
+
+       }
+
+       ret = pthread_mutex_lock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
+               return;
+       }
+
+       info = (struct logging_infos *)
+               g_hash_table_lookup(logging_proc_list, name);
+
+       /* To Do: handle multiple daemons with the same name */
+       if (!info) {
+               insert_hash_table(name, pid, oom);
+       } else {
+               info->running = true;
+               info->pid = pid;
+               info->oom = oom;
+       }
+
+       ret = pthread_mutex_unlock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+               return;
+       }
+       return;
+}
+
+static bool is_running_process(GArray *garray, pid_t pid)
+{
+       int i;
+       pid_t tpid;
+
+       for (i = 0 ; i < garray->len; i++) {
+               tpid = g_array_index(garray, pid_t, i);
+               if (tpid == pid)
+                       return true;
+       }
+       return false;
+}
+
+static void update_proc_state(void)
+{
+       DIR *dirp;
+       struct dirent entry;
+       struct dirent *result;
+       GArray *running_procs = NULL;
+       GHashTableIter iter;
+       int ret;
+       gpointer key, value;
+
+       running_procs = g_array_new(false, false, sizeof(pid_t));
+
+       if (!running_procs) {
+               _E("fail to create garray for pids");
+               return;
+       }
+
+       dirp = opendir("/proc");
+
+       if (dirp == NULL) {
+               _E("/proc open is failed, and cannot updated running procs");
+               return;
+       }
+
+       while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
+               pid_t pid;
+
+               if (!isdigit(entry.d_name[0]))
+                       continue;
+               pid = atoi(entry.d_name);
+               g_array_append_val(running_procs, pid);
+       }
+
+       closedir(dirp);
+
+       if (ret) {
+               _E("readdir_r on /proc directory failed");
+               g_array_free(running_procs, true);
+               return;
+       }
+
+       g_hash_table_iter_init(&iter, logging_proc_list);
+
+       ret = pthread_mutex_lock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
+               g_array_free(running_procs, true);
+               return;
+       }
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               struct logging_infos *info = (struct logging_infos *)value;
+               info->running = is_running_process(running_procs, info->pid);
+       }
+
+       g_array_free(running_procs, true);
+
+       ret = pthread_mutex_unlock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+               return;
+       }
+
+       need_to_update = false;
+       return;
+}
+
+static void update_proc_list(void)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       time_t now;
+       struct logging_infos *infos;
+       int ret;
+
+       if (need_to_update)
+               update_proc_state();
+
+       now = time(NULL);
+
+       g_hash_table_iter_init(&iter, logging_proc_list);
+
+       while (1) {
+               ret = pthread_mutex_lock(&proc_list_mutex);
+               if (ret) {
+                       _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
+                       return;
+               }
+
+               if (!g_hash_table_iter_next(&iter, &key, &value)) {
+                       ret = pthread_mutex_unlock(&proc_list_mutex);
+                       if (ret) {
+                               _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+                               return;
+                       }
+                       _D("finish proc list update");
+                       break;
+               }
+               infos = (struct logging_infos *)value;
+
+               if (infos->running)
+                       update_logging_infos(infos, now, false);
+               ret = pthread_mutex_unlock(&proc_list_mutex);
+               if (ret) {
+                       _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+                       return;
+               }
+       }
+
+       return;
+}
+
+static void logging_update_state(void)
+{
+       need_to_update = true;
+}
+
+static int check_running(gpointer key, gpointer value, gpointer user_data)
+{
+       struct logging_infos *infos = (struct logging_infos *)value;
+
+       return !(infos->running);
+}
+
+static void reclaim_proc_list(void)
+{
+       int ret;
+
+       ret = pthread_mutex_lock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_lock() failed, %d", ret);
+               return;
+       }
+
+       g_hash_table_foreach_remove(logging_proc_list, check_running, NULL);
+       ret = pthread_mutex_unlock(&proc_list_mutex);
+       if (ret) {
+               _E("proc_list_mutex::pthread_mutex_unlock() failed, %d", ret);
+               return;
+       }
+}
+
+static void logging(void)
+{
+       update_proc_list();
+
+       if (g_hash_table_size(logging_proc_list) > MAX_PROC_LIST) {
+               write_logging_infos(true);
+               reclaim_proc_list();
+       } else
+               write_logging_infos(false);
+}
+
+static void *logging_pthread(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_mutex);
+               if ( ret ) {
+                       _E("logging thread::pthread_mutex_lock() failed, %d", ret);
+                       break;
+               }
+
+               ret = pthread_cond_wait(&logging_cond, &logging_mutex);
+               if ( ret ) {
+                       _E("logging thread::pthread_cond_wait() failed, %d", ret);
+                       ret = pthread_mutex_unlock(&logging_mutex);
+                       if ( ret )
+                               _E("logging thread::pthread_mutex_lock() failed, %d", ret);
+                       break;
+               }
+
+               logging();
+
+               ret = pthread_mutex_unlock(&logging_mutex);
+               if ( ret ) {
+                       _E("logging thread::pthread_mutex_unlock() failed, %d", ret);
+                       break;
+               }
+       }
+
+       /* Now our thread finishes - cleanup TID */
+       logging_thread = 0;
+
+       return NULL;
+}
+
+
+static int logging_thread_create(void)
+{
+       int ret = RESOURCED_ERROR_NONE;
+
+       if (logging_thread) {
+               _I("logging thread %u already created", (unsigned)logging_thread);
+       } else {
+               /* initialize logging_thread */
+               ret = pthread_create(&logging_thread, NULL, (void *)logging_pthread, (void *)NULL);
+               if (ret) {
+                       _E("pthread creation for logging_pthread failed, %d\n", ret);
+                       logging_thread = 0;
+               } else {
+                       _D("pthread creation for logging success");
+                       pthread_detach(logging_thread);
+               }
+       }
+
+       return ret;
+}
+
+static void free_key(gpointer key)
+{
+       if (!key)
+               free(key);
+}
+
+static void free_value(gpointer value)
+{
+       int i;
+       struct logging_infos * info = (struct logging_infos *)value;
+
+       if (!info)
+               return;
+
+       for (i = 0; i < num_log_infos; i++) {
+               if (info->stats[i])
+                       free(info->stats[i]);
+       }
+
+       if (info->stats)
+               free(info->stats);
+
+       free(info);
+}
+
+static void initialize_logging_proc_list(void)
+{
+       DIR *dirp;
+       struct dirent entry;
+       struct dirent *result;
+       char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+       int cur_oom = -1;
+       FILE *fp = NULL;
+       int ret;
+
+       dirp = opendir("/proc");
+
+       if (dirp == NULL) {
+               _E("/proc open is failed, and cannot updated running procs");
+               return;
+       }
+
+       while (!(ret = readdir_r(dirp, &entry, &result)) && result != NULL) {
+               pid_t pid, pgid;
+
+               if (!isdigit(entry.d_name[0]))
+                       continue;
+               pid = atoi(entry.d_name);
+               pgid = getpgid(pid);
+               if (!pgid)
+                       continue;
+               snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+               fp = fopen(buf, "r+");
+               if (fp == NULL)
+                       continue;
+               if (fgets(buf, sizeof(buf), fp) == NULL) {
+                       fclose(fp);
+                       continue;
+               }
+               cur_oom = atoi(buf);
+               fclose(fp);
+               insert_proc_list(pid, pgid, cur_oom);
+       }
+
+       closedir(dirp);
+       write_logging_infos(true);
+       return;
+}
+
+static void logging_update_start(void)
+{
+       int ret;
+       /* signal to logging_pthread to start */
+       ret = pthread_mutex_lock(&logging_mutex);
+       if (ret) {
+               _E("logging_update_start::pthread_mutex_lock() failed, %d", ret);
+               return;
+       }
+
+       ret = pthread_cond_signal(&logging_cond);
+       if (ret) {
+               _E("logging_update_start::pthread_cond_wait() failed, %d", ret);
+               ret = pthread_mutex_unlock(&logging_mutex);
+               if ( ret )
+                       _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
+               return;
+       }
+
+       _D("send signal logging_pthread");
+       ret = pthread_mutex_unlock(&logging_mutex);
+       if (ret) {
+               _E("logging_update_start::pthread_mutex_unlock() failed, %d", ret);
+               return;
+       }
+}
+
+static void broadcast_logging_data_updated_signal(void)
+{
+       int r;
+
+       r = broadcast_edbus_signal_str(RESOURCED_PATH_LOGGING, RESOURCED_INTERFACE_LOGGING,
+                       SIGNAL_LOGGING_UPDATED, NULL, NULL);
+       _I("broadcast logging_data updated signal!");
+
+       if (r < 0)
+               _E("Failed: broadcast logging_data_updated signal");
+}
+
+static void logging_init_booting_done_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+       int ret;
+
+       ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
+               SIGNAL_LOGGING_INIT);
+       if (ret == 0) {
+               _D("there is booting done signal");
+               return;
+       }
+       initialize_logging_proc_list();
+       _D("logging_init_booting_done_edbus_signal_handler");
+}
+
+static void logging_get_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+       int ret;
+
+       ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_LOGGING,
+               SIGNAL_LOGGING_GET);
+       if (ret == 0) {
+               _D("there is logging get signal");
+               return;
+       }
+       write_logging_infos(true);
+       _D("logging_get_edbus_signal_handler");
+       broadcast_logging_data_updated_signal();
+}
+
+static int logging_init(void)
+{
+       int ret = RESOURCED_ERROR_NONE;
+
+       _D("logging_init start");
+
+       logging_proc_list = g_hash_table_new_full(
+               g_str_hash,
+               g_str_equal,
+               free_key,
+               free_value);
+
+       if (!logging_proc_list) {
+               _E("fail g_hash_table_new_full() for logging_proc_list");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       logging_ss_list = g_array_new(false, false,
+               sizeof(struct logging_sub_sys));
+
+       if (logging_ss_list == NULL)
+               return RESOURCED_ERROR_FAIL;
+
+       ret = logging_thread_create();
+       if (ret) {
+               _E("logging thread create failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
+               RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_INIT,
+                   (void *)logging_init_booting_done_edbus_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_LOGGING,
+               RESOURCED_INTERFACE_LOGGING, SIGNAL_LOGGING_GET,
+                   (void *)logging_get_edbus_signal_handler);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_logging_control(void *data)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct logging_data_type *l_data;
+
+       if (!num_log_infos)
+               return ret;
+
+       l_data = (struct logging_data_type *)data;
+
+       switch(l_data->control_type) {
+       case LOGGING_INSERT_PROC_LIST:
+               if (l_data->args)
+                       insert_proc_list((pid_t)l_data->args[0],
+                               (pid_t)l_data->args[1], (int)l_data->args[2]);
+               break;
+       case LOGGING_UPDATE_PROC_INFO:
+               logging_update_start();
+               break;
+       case LOGGING_UPDATE_STATE:
+               logging_update_state();
+               break;
+       }
+       return ret;
+}
+
+static int resourced_logging_init(void *data)
+{
+       logging_ops = &logging_modules_ops;
+
+       return logging_init();
+}
+
+static int resourced_logging_exit(void *data)
+{
+       if (logging_ss_list)
+               g_array_free(logging_ss_list, TRUE);
+       if (logging_proc_list)
+               g_hash_table_destroy(logging_proc_list);
+       return RESOURCED_ERROR_NONE;
+}
+
+int logging_control(enum logging_control_type type, unsigned long *args)
+{
+       struct logging_data_type l_data;
+
+       if (logging_ops) {
+               l_data.control_type = type;
+               l_data.args = args;
+               return logging_ops->control(&l_data);
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops logging_modules_ops = {
+       .priority       = MODULE_PRIORITY_HIGH,
+       .name           = "logging",
+       .init           = resourced_logging_init,
+       .exit           = resourced_logging_exit,
+       .control        = resourced_logging_control,
+};
+
+MODULE_REGISTER(&logging_modules_ops)
diff --git a/src/memory/logging-memory.c b/src/memory/logging-memory.c
new file mode 100644 (file)
index 0000000..f73f8f6
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * 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 logging-memory.c
+ *
+ * @desc start memory logging system for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <linux/limits.h>
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include <dirent.h>
+#include <sys/utsname.h>
+#include <systemd/sd-journal.h>
+
+#include "resourced.h"
+#include "trace.h"
+#include "file-helper.h"
+#include "module.h"
+#include "macro.h"
+#include "proc-process.h"
+#include "logging.h"
+
+#define        MEM_NAME "memory"
+#define        MEM_COMMIT_INTERVAL             30*60   /* 30 min */
+
+#define        MEM_MAX_INTERVAL                10*60   /* 10 min */
+#define        MEM_INIT_INTERVAL               8*60    /* 8 min */
+#define MEM_FOREGRD_INTERVAL           5*60    /* 5 min */
+#define MEM_BACKGRD_INTERVAL           10*60   /* 10 min */
+#define MEM_BACKGRD_OLD_INTERVAL       15*60   /* 15 min */
+
+struct logging_memory_info {
+       unsigned avg_pss;
+       unsigned max_pss;
+       unsigned avg_uss;
+       unsigned max_uss;
+       unsigned sampling_count;
+       bool last_commited;
+       time_t last_log_time;
+       time_t log_interval;
+       pid_t current_pid;
+};
+
+struct mapinfo {
+       unsigned size;
+       unsigned rss;
+       unsigned pss;
+       unsigned shared_clean;
+       unsigned shared_dirty;
+       unsigned private_clean;
+       unsigned private_dirty;
+};
+
+static int ignore_smaps_field;
+static struct mapinfo *mi;
+static struct mapinfo *maps;
+
+static void check_kernel_version(void)
+{
+       struct utsname buf;
+       int ret;
+
+       ret = uname(&buf);
+
+       if (ret)
+               return;
+
+       if (buf.release[0] == '3') {
+               char *pch;
+               char str[3];
+               int sub_version;
+               pch = strstr(buf.release, ".");
+               strncpy(str, pch+1, 2);
+               sub_version = atoi(str);
+
+               if (sub_version >= 10)
+                       ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
+                                                  Swap, KernelPageSize, MMUPageSize,
+                                                  Locked, VmFlags */
+
+               else
+                       ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
+                                                  Swap, KernelPageSize, MMUPageSize,
+                                                  Locked */
+       } else {
+               ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
+                                          MMUPageSize */
+       }
+}
+
+
+/* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
+ * 012345678901234567890123456789012345678901234567890123456789
+ * 0         1         2         3         4         5
+ */
+
+static int read_mapinfo(char** smaps, int rest_line)
+{
+       char* line;
+       int len;
+
+       if ((line = cgets(smaps)) == 0)
+               return RESOURCED_ERROR_FAIL;
+
+       len    = strlen(line);
+       if (len < 1) {
+               _E("line is less than 1");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Size: %d kB", &mi->size) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
+               if ((line = cgets(smaps)) == 0)
+                       goto oops;
+       if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
+               goto oops;
+
+       while (rest_line-- && cgets(smaps))
+               ;
+
+       return RESOURCED_ERROR_NONE;
+ oops:
+       _E("mi get error\n");
+       return RESOURCED_ERROR_FAIL;
+}
+
+static void init_maps()
+{
+       maps->size = 0;
+       maps->rss = 0;
+       maps->pss = 0;
+       maps->shared_clean = 0;
+       maps->shared_dirty = 0;
+       maps->private_clean = 0;
+       maps->private_dirty = 0;
+}
+
+static int load_maps(int pid)
+{
+       char* smaps, *start;
+       char tmp[128];
+
+       snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
+       smaps = cread(tmp);
+       if (smaps == NULL)
+               return RESOURCED_ERROR_FAIL;
+
+       start = smaps;
+       init_maps();
+
+       while (read_mapinfo(&smaps, ignore_smaps_field)
+                       == RESOURCED_ERROR_NONE) {
+               maps->size += mi->size;
+               maps->rss += mi->rss;
+               maps->pss += mi->pss;
+               maps->shared_clean += mi->shared_clean;
+               maps->shared_dirty += mi->shared_dirty;
+               maps->private_clean += mi->private_clean;
+               maps->private_dirty += mi->private_dirty;
+       }
+
+       if(start)
+               free(start);
+       return RESOURCED_ERROR_NONE;
+}
+
+int get_pss(pid_t pid, unsigned *pss, unsigned *uss)
+{
+       int ret;
+       ret = load_maps(pid);
+       if (ret != RESOURCED_ERROR_NONE) {
+               *pss = 0;
+               *uss = 0;
+       } else {
+               *pss = maps->pss;
+               *uss = maps->private_clean + maps->private_dirty;
+       }
+
+       return ret;
+}
+
+static void update_log_interval(struct logging_memory_info *loginfo, int oom,
+       int always)
+{
+       if (!always && (oom < OOMADJ_FOREGRD_LOCKED))
+               return;
+
+       switch (oom) {
+       case OOMADJ_DISABLE:
+       case OOMADJ_SERVICE_MIN:
+       case OOMADJ_SU:
+               loginfo->log_interval = MEM_MAX_INTERVAL;
+               break;
+       case OOMADJ_INIT:
+               loginfo->log_interval = MEM_INIT_INTERVAL;
+               break;
+       case OOMADJ_FOREGRD_LOCKED:
+       case OOMADJ_FOREGRD_UNLOCKED:
+       case OOMADJ_BACKGRD_LOCKED:
+               loginfo->log_interval = MEM_FOREGRD_INTERVAL;
+               break;
+       case OOMADJ_BACKGRD_UNLOCKED:
+               loginfo->log_interval = MEM_BACKGRD_INTERVAL;
+               break;
+       default:
+               if (oom > OOMADJ_BACKGRD_UNLOCKED)
+                       loginfo->log_interval = MEM_BACKGRD_OLD_INTERVAL;
+               break;
+       }
+}
+
+static int init_memory_info(void **pl, pid_t pid, int oom, time_t now)
+{
+       struct logging_memory_info *info;
+
+       info = (struct logging_memory_info *)
+                       malloc(sizeof(struct logging_memory_info));
+       if (!info) {
+               _E("malloc for logging_memory_info is failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+       info->current_pid = 0;
+       info->avg_pss = 0;
+       info->max_pss = 0;
+       info->avg_uss = 0;
+       info->max_uss = 0;
+       info->last_log_time = now;
+       info->sampling_count = 0;
+       info->last_commited = false;
+
+       update_log_interval(info, oom, 1);
+       *pl = (void *)info;
+       return RESOURCED_ERROR_NONE;
+}
+
+/* pss_interval should be adjusted depending on app type */
+static int update_memory_info(void *pl, pid_t pid, int oom,
+       time_t now, unsigned always)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct logging_memory_info *loginfo = (struct logging_memory_info *)pl;
+       unsigned pss = 0, uss = 0;
+
+       if (!always)
+               if (now < loginfo->last_log_time + loginfo->log_interval)
+                       return ret;
+
+       ret = get_pss(pid, &pss, &uss);
+
+       if (ret != RESOURCED_ERROR_NONE)
+               return ret;
+
+       loginfo->avg_pss = (loginfo->avg_pss * loginfo->sampling_count +
+                       pss)/(loginfo->sampling_count + 1);
+       loginfo->avg_uss = (loginfo->avg_uss * loginfo->sampling_count +
+                       uss)/(loginfo->sampling_count + 1);
+       if (pss > loginfo->max_pss)
+               loginfo->max_pss = pss;
+       if (uss > loginfo->max_uss)
+               loginfo->max_uss = uss;
+
+       loginfo->sampling_count++;
+       loginfo->last_log_time = now;
+       loginfo->last_commited = false;
+       update_log_interval(loginfo, oom, 0);
+
+       return ret;
+}
+
+static int write_memory_info(char *name, struct logging_infos *infos,
+       int ss_index)
+{
+       struct logging_memory_info *mi = infos->stats[ss_index];
+
+       if (!infos->running && mi->last_commited)
+               return RESOURCED_ERROR_NONE;
+
+       sd_journal_send("NAME=memory",
+               "TIME=%ld", mi->last_log_time,
+               "PNAME=%s", name,
+               "AVG_PSS=%lu", mi->avg_pss,
+               "MAX_PSS=%lu", mi->max_pss,
+               "AVG_USS=%lu", mi->avg_uss,
+               "MAX_USS=%lu", mi->max_uss,
+               NULL);
+
+       mi->last_commited = true;
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static struct logging_info_ops memory_info_ops = {
+       .update = update_memory_info,
+       .write  = write_memory_info,
+       .init   = init_memory_info,
+};
+
+static int allocate_memory(void)
+{
+       maps = (struct mapinfo *)malloc(sizeof(struct mapinfo));
+
+       if (!maps) {
+               _E("fail to allocate mapinfo\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       mi = malloc(sizeof(struct mapinfo));
+       if (mi == NULL) {
+               _E("malloc failed for mapinfo");
+               free(maps);
+               return RESOURCED_ERROR_FAIL;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static void free_memory(void)
+{
+       free(maps);
+       free(mi);
+}
+
+static int logging_memory_init(void *data)
+{
+       int ret;
+       check_kernel_version();
+
+       ret = allocate_memory();
+
+       if (ret != RESOURCED_ERROR_NONE) {
+               _E("allocate structures failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       ret = register_logging_subsystem(MEM_NAME, &memory_info_ops);
+       if(ret != RESOURCED_ERROR_NONE) {
+               _E("register logging subsystem failed");
+               free_memory();
+               return RESOURCED_ERROR_FAIL;
+       }
+       ret = update_commit_interval(MEM_NAME, MEM_COMMIT_INTERVAL);
+       if(ret != RESOURCED_ERROR_NONE) {
+               _E("update commit interval logging subsystem failed");
+               free_memory();
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       _D("logging memory init finished");
+       return RESOURCED_ERROR_NONE;
+}
+
+static int logging_memory_exit(void *data)
+{
+       _D("logging memory finalize");
+       free_memory();
+       return RESOURCED_ERROR_NONE;
+}
+
+static struct module_ops logging_memory_ops = {
+       .priority       = MODULE_PRIORITY_NORMAL,
+       .name           = "logging_memory",
+       .init           = logging_memory_init,
+       .exit           = logging_memory_exit,
+};
+
+MODULE_REGISTER(&logging_memory_ops)
diff --git a/src/memory/lowmem-dbus.c b/src/memory/lowmem-dbus.c
new file mode 100644 (file)
index 0000000..d262625
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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"
+
+#define SIGNAL_NAME_OOM_SET_THRESHOLD                  "SetThreshold"
+#define SIGNAL_NAME_OOM_SET_LEAVE_THRESHOLD            "SetLeaveThreshold"
+#define SIGNAL_NAME_OOM_TRIGGER                        "Trigger"
+
+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_NAME_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;
+       }
+
+       set_threshold(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_NAME_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;
+       }
+
+       set_leave_threshold(thres);
+}
+
+static void lowmem_dbus_oom_trigger(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int ret;
+       int launching = 0;
+       int flags = OOM_FORCE;
+
+       ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_OOM, SIGNAL_NAME_OOM_TRIGGER);
+       if (ret == 0) {
+               _D("there is no oom trigger signal");
+               return;
+       }
+
+       dbus_error_init(&err);
+
+       ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &launching, DBUS_TYPE_INVALID);
+
+       if (launching)
+               flags |= OOM_NOMEMORY_CHECK;
+
+       change_memory_state(MEMNOTIFY_LOW, 1);
+       lowmem_oom_killer_cb(MEMCG_MEMORY, flags);
+       _D("flags = %d", flags);
+       change_memory_state(MEMNOTIFY_NORMAL, 0);
+}
+
+void lowmem_dbus_init(void)
+{
+       register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+                       SIGNAL_NAME_OOM_SET_THRESHOLD,
+                   lowmem_dbus_oom_set_threshold);
+       register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+                       SIGNAL_NAME_OOM_SET_LEAVE_THRESHOLD,
+                   lowmem_dbus_oom_set_leave_threshold);
+       register_edbus_signal_handler(RESOURCED_PATH_OOM, RESOURCED_INTERFACE_OOM,
+                       SIGNAL_NAME_OOM_TRIGGER,
+                   lowmem_dbus_oom_trigger);
+
+}
diff --git a/src/memory/lowmem-handler.c b/src/memory/lowmem-handler.c
new file mode 100644 (file)
index 0000000..9d5faca
--- /dev/null
@@ -0,0 +1,1395 @@
+/*
+ * 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_handler.c
+ *
+ * @desc lowmem handler using memcgroup
+ *
+ * Copyright (c) 2013 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 <fcntl.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 <Ecore.h>
+
+#include "trace.h"
+#include "cgroup.h"
+#include "proc-main.h"
+#include "lowmem-handler.h"
+#include "proc-process.h"
+#include "swap-common.h"
+#include "lowmem-common.h"
+#include "resourced.h"
+#include "macro.h"
+#include "module.h"
+#include "notifier.h"
+
+enum {
+       MEMGC_OOM_NORMAL,
+       MEMGC_OOM_SOFTSWAP,
+       MEMGC_OOM_WARNING,
+       MEMGC_OOM_HIGH,
+       MEMGC_OOM_CRITICAL,
+};
+
+enum {
+       MEMGC_GROUP_FOREGROUND,
+       MEMGC_GROUP_BACKGROUND,
+};
+
+enum {
+       MEM_SWAP_OFF,
+       MEM_SWAP_ENABLE,
+       MEM_SWAP_DECREASE,
+       MEM_SWAP_INCREASE,
+};
+
+#define MEM_SOFTSWAP_ENABLE 1
+#define MEMCG_GROUP_MAX                2
+
+#define MEMINFO_PATH   "/proc/meminfo"
+#define MEMCG_PATH             "/sys/fs/cgroup/memory"
+#define VICTIM_TASK            "/sys/class/lowmemnotify/victims"
+#define SET_LEAVE_THRESHOLD    "/sys/class/lowmemnotify/leave_threshold"
+#define SET_CGROUP_LEAVE_THRESHOLD "/sys/class/lowmemnotify/cgroup_leave_threshold"
+#define SET_THRESHOLD_LV1 "/sys/class/lowmemnotify/threshold_level1"
+#define SET_THRESHOLD_LV2 "/sys/class/lowmemnotify/threshold_level2"
+#define SET_THRESHOLD_RECLAIM "/sys/class/lowmemnotify/threshold_reclaim"
+
+#define MEMPS_LOG_FILE "/var/log/memps"
+
+#define DELETE_SM              "sh -c "PREFIX"/bin/delete.sm"
+#define MEMPS_EXEC_PATH                "usr/bin/memps"
+
+
+#define _SYS_RES_CLEANUP       "RES_CLEANUP"
+
+#define BtoMB(x)               ((x) / 1024 / 1024)
+#define MBtoB(x)               (x<<20)
+
+#define MEMCG_FOREGROUND_LIMIT_RATIO   0.6
+#define MEMCG_BACKGROUND_LIMIT_RATIO   0.7
+
+#define MEMCG_FOREGROUND_MIN_LIMIT     MBtoB(400)
+#define MEMCG_BACKGROUND_MIN_LIMIT     UINT_MAX
+
+/* threshold lv 1 : wakeup softswapd */
+#define MEMCG_TRHES_SOFTSWAP_RATIO             0.75
+
+/* threshold lv 2 : lowmem warning */
+#define MEMCG_THRES_WARNING_RATIO              0.92
+
+/* threshold lv 3 : victim kill */
+#define MEMCG_THRES_OOM_RATIO                  0.96
+
+/* leave threshold */
+#define MEMCG_OOMLEAVE_RATIO                   0.88
+
+#define MEMNOTIFY_NORMAL       0x0000
+#define MEMNOTIFY_RECLAIM      0xecae
+#define MEMNOTIFY_LOW          0xfaac
+#define MEMNOTIFY_CRITICAL     0xdead
+
+/* define threshold limit */
+#define MAX_OOM_THRES                          0x04600000      /* 70M */
+#define MIN_OOM_THRES                          0x03000000      /* 48M */
+#define MAX_WARN_THRES                         0x07800000      /* 120M */
+#define MAX_LEAVE_THRES                                0x0B400000      /* 180M */
+#define MIN_OOM_WARN_GAP                       0x01400000      /* 30M */
+
+#define MEM_THRESHOLD_RECLAIM                  300
+#define MEM_THRESHOLD_LV1                      180
+#define MEM_THRESHOLD_LV2                      160
+#define MEM_LEAVE_THRESHOLD                    200
+#define LOWMEM_PATH_MAX                                100
+
+#define MAX_VICTIMS            30
+
+static int lowmem_fd = -1;
+static Ecore_Fd_Handler *lowmem_efd;
+static int cur_mem_state = MEMNOTIFY_NORMAL;
+
+static Ecore_Timer *oom_check_timer;
+#define OOM_TIMER_INTERVAL     3
+#define OOM_MULTIKILL_WAIT     (1000*1000)
+#define OOM_CHECK_PROC_WAIT    (2000*1000)
+
+unsigned int oom_delete_sm_time;
+
+/* low memory action function */
+static int memory_low_act(void *ad);
+static int memory_oom_act(void *ad);
+static int memory_normal_act(void *ad);
+static int memory_reclaim_act(void *ad);
+
+
+/* low memory action function for cgroup */
+static int memory_cgroup_oom_act(int memcg_index);
+
+static int lowmem_fd_start();
+static int lowmem_fd_stop(int fd);
+
+struct memcg_class {
+       unsigned int event_fd;
+       unsigned int min_limit;
+       float   limit_ratio;
+       unsigned int oomlevel;
+       unsigned int oomalert;
+       unsigned int oomleave;
+       char *cgroup_name;
+       unsigned int total_limit;
+       unsigned int thres_lv1;
+       unsigned int thres_lv2;
+       unsigned int thres_lv3;
+       unsigned int thres_leave;
+};
+
+struct lowmem_process_entry {
+       unsigned cur_mem_state;
+       unsigned new_mem_state;
+       int (*action) (void *);
+};
+
+static struct lowmem_process_entry lpe[] = {
+       {MEMNOTIFY_NORMAL,      MEMNOTIFY_RECLAIM,      memory_reclaim_act},
+       {MEMNOTIFY_NORMAL,      MEMNOTIFY_LOW,          memory_low_act},
+       {MEMNOTIFY_NORMAL,      MEMNOTIFY_CRITICAL,     memory_oom_act},
+       {MEMNOTIFY_RECLAIM,     MEMNOTIFY_LOW,          memory_low_act},
+       {MEMNOTIFY_RECLAIM,     MEMNOTIFY_CRITICAL,     memory_oom_act},
+       {MEMNOTIFY_LOW,         MEMNOTIFY_CRITICAL,     memory_oom_act},
+       {MEMNOTIFY_CRITICAL,    MEMNOTIFY_CRITICAL,     memory_oom_act},
+       {MEMNOTIFY_LOW,         MEMNOTIFY_RECLAIM,      memory_reclaim_act},
+       {MEMNOTIFY_LOW,         MEMNOTIFY_NORMAL,       memory_normal_act},
+       {MEMNOTIFY_CRITICAL,    MEMNOTIFY_NORMAL,       memory_normal_act},
+       {MEMNOTIFY_CRITICAL,    MEMNOTIFY_RECLAIM,      memory_reclaim_act},
+       {MEMNOTIFY_RECLAIM,     MEMNOTIFY_NORMAL,       memory_normal_act},
+};
+
+static struct memcg_class memcg_class[MEMCG_GROUP_MAX] = {
+       {0, MEMCG_FOREGROUND_MIN_LIMIT, MEMCG_FOREGROUND_LIMIT_RATIO, 0, 0, 0, "foreground",
+               0, 0, 0, 0, 0},
+       {0, MEMCG_BACKGROUND_MIN_LIMIT, MEMCG_BACKGROUND_LIMIT_RATIO, 0, 0, 0, "background",
+               0, 0, 0, 0, 0},
+};
+
+static const struct module_ops memory_modules_ops;
+static const struct module_ops *lowmem_ops;
+
+unsigned int get_available(void)
+{
+       char buf[PATH_MAX];
+       FILE *fp;
+       char *idx;
+       unsigned int free = 0, cached = 0;
+       unsigned int available = 0;
+
+       fp = fopen(MEMINFO_PATH, "r");
+       if (!fp) {
+               _E("%s open failed, %d", buf, fp);
+               return available;
+       }
+
+       while (fgets(buf, PATH_MAX, fp) != NULL) {
+               if ((idx = strstr(buf, "MemFree:"))) {
+                       idx += strlen("MemFree:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       free = atoi(idx);
+               } else if ((idx = strstr(buf, "MemAvailable:"))) {
+                       idx += strlen("MemAvailable:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       available = atoi(idx);
+                       break;
+               } else if((idx = strstr(buf, "Cached:"))) {
+                       idx += strlen("Cached:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       cached = atoi(idx);
+                       break;
+               }
+       }
+
+       if (available == 0)
+               available = free + cached;
+       available >>= 10;
+       fclose(fp);
+
+       return available;
+}
+
+void lowmem_dynamic_process_killer(int type)
+{
+       /* This function is not supported */
+}
+
+void change_memory_state(int state, int force)
+{
+       int mem_state;
+
+       if (force) {
+               mem_state = state;
+       } else {
+               mem_state = cur_mem_state;
+               _D("mem_state = %d", mem_state);
+       }
+
+       switch (mem_state) {
+       case MEMNOTIFY_NORMAL:
+               memory_normal_act(NULL);
+               break;
+       case MEMNOTIFY_RECLAIM:
+               memory_reclaim_act(NULL);
+               break;
+       case MEMNOTIFY_LOW:
+               memory_low_act(NULL);
+               break;
+       case MEMNOTIFY_CRITICAL:
+               memory_oom_act(NULL);
+               break;
+       default:
+               assert(0);
+       }
+}
+
+static unsigned int _get_total_memory(void)
+{
+       char buf[PATH_MAX];
+       FILE *fp;
+       char *idx;
+       unsigned int total = 0;
+
+       fp = fopen(MEMINFO_PATH, "r");
+
+       if (!fp)
+               return total;
+
+       while (fgets(buf, PATH_MAX, fp) != NULL) {
+               if ((idx = strstr(buf, "MemTotal:"))) {
+                       idx += strlen("MemTotal:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       total = atoi(idx);
+                       total *= 1024;
+                       break;
+               }
+       }
+       fclose(fp);
+       return total;
+}
+
+static void _calc_threshold(int type, int limit)
+{
+       unsigned int val, check;
+
+       /* calculate theshold lv3 */
+       val = (unsigned int)(memcg_class[type].total_limit*
+                       (float)MEMCG_THRES_OOM_RATIO);
+
+       /* check MIN & MAX value about threshold lv3*/
+       if (limit - val > MAX_OOM_THRES)
+               val = (unsigned int)(limit - MAX_OOM_THRES);
+       else if (limit - val < MIN_OOM_THRES)
+               val = (unsigned int)(limit - MIN_OOM_THRES);
+
+       /* set threshold lv3 */
+       memcg_class[type].thres_lv3 = val;
+
+       /* calculate threshold lv2 */
+       val = (unsigned int)(memcg_class[type].total_limit*
+                       (float)MEMCG_THRES_WARNING_RATIO);
+
+       check = memcg_class[type].thres_lv3;
+
+       /* check MIN & MAX value about threshold lv2*/
+       if (check - val < MIN_OOM_WARN_GAP)
+               val = (unsigned int)(check - MIN_OOM_WARN_GAP);
+       else if (limit - val > MAX_WARN_THRES)
+               val = (unsigned int)(limit - MAX_WARN_THRES);
+
+       /* set threshold lv2 */
+       memcg_class[type].thres_lv2 = val;
+
+       /* calculate threshold lv1 */
+       val = (unsigned int)(memcg_class[type].total_limit*
+                       (float)MEMCG_TRHES_SOFTSWAP_RATIO);
+
+       /* check MIN value about threshold lv1*/
+       check = memcg_class[type].thres_lv2;
+
+       if (check - val < MIN_OOM_WARN_GAP)
+               val = (unsigned int)(check - MIN_OOM_WARN_GAP);
+
+       memcg_class[type].thres_lv1 = val;
+
+       /* set leave threshold */
+       val = (unsigned int)(memcg_class[type].total_limit*
+                       (float)MEMCG_OOMLEAVE_RATIO);
+
+       check = memcg_class[type].thres_lv1;
+
+       /* check MIN & MAX value about leave threshold */
+       if (check - val < MIN_OOM_WARN_GAP)
+               val = (unsigned int)(check - MIN_OOM_WARN_GAP);
+       else if (limit - val > MAX_LEAVE_THRES)
+               val = (unsigned int)(limit - MAX_WARN_THRES);
+
+       memcg_class[type].oomleave = val;
+}
+
+static unsigned int get_mem_usage(int idx)
+{
+       FILE *f;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       unsigned int usage;
+
+       snprintf(buf, sizeof(buf), "%s/%s/memory.usage_in_bytes",
+                       MEMCG_PATH, memcg_class[idx].cgroup_name);
+
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed, %d", buf, f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       if (fgets(buf, 32, f) == NULL) {
+               _E("fgets failed\n");
+               fclose(f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       usage = atoi(buf);
+       fclose(f);
+
+       return usage;
+}
+
+static int get_current_oom(int idx)
+{
+       FILE *f;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       char *oom;
+       unsigned int level;
+
+       snprintf(buf, sizeof(buf), "%s/%s/memory.oom_usr_control",
+                       MEMCG_PATH, memcg_class[idx].cgroup_name);
+
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed, %d", buf, f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       if (fgets(buf, 32, f) == NULL) {
+               _E("fgets failed\n");
+               fclose(f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       oom = strstr(buf, "oom_usr_control");
+       oom += strlen("oom_usr_control");
+       while (*oom < '0' || *oom > '9')
+               oom++;
+       level = atoi(oom);
+       fclose(f);
+       _D("get_current_oom : %d", level);
+       return level;
+}
+
+static int remove_shm(void)
+{
+       int maxid, shmid, id;
+       struct shmid_ds shmseg;
+       struct shm_info shm_info;
+
+       maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info);
+       if (maxid < 0) {
+               _E("shared mem error\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       for (id = 0; id <= maxid; id++) {
+               shmid = shmctl(id, SHM_STAT, &shmseg);
+               if (shmid < 0)
+                       continue;
+               if (shmseg.shm_nattch == 0) {
+                       _D("shared memory killer ==> %d killed\n",
+                                 shmid);
+                       shmctl(shmid, IPC_RMID, NULL);
+               }
+       }
+       return 0;
+}
+
+static void print_mem_state(void)
+{
+       unsigned int usage, i;
+
+       for (i = 0; i < MEMCG_GROUP_MAX; i++) {
+               usage = get_mem_usage(i);
+               _I("[MEM STATE] memcg : %s, usage %d oom level : %d",
+                               memcg_class[i].cgroup_name, usage,
+                               memcg_class[i].oomlevel);
+       }
+}
+
+static void make_memps_log(char *file, pid_t pid, char *victim_name)
+{
+       time_t now;
+       struct tm *cur_tm;
+       char new_log[512];
+       static pid_t old_pid;
+
+       if (old_pid == pid)
+               return;
+       old_pid = pid;
+
+       now = time(NULL);
+       cur_tm = (struct tm *)malloc(sizeof(struct tm));
+       if (cur_tm == NULL) {
+               _E("Fail to memory allocation");
+               return;
+       }
+
+       if (localtime_r(&now, cur_tm) == NULL) {
+               _E("Fail to get localtime");
+               free(cur_tm);
+               return;
+       }
+
+       snprintf(new_log, sizeof(new_log),
+                "%s_%s_%d_%.4d%.2d%.2d_%.2d%.2d%.2d.log", 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);
+
+       free(cur_tm);
+       if (fork() == 0) {
+               execl(MEMPS_EXEC_PATH, MEMPS_EXEC_PATH, "-f", new_log, (char *)NULL);
+               exit(0);
+       }
+}
+
+static int lowmem_check_current_state(int memcg_index,
+               int total_size, int oom_usage)
+{
+       unsigned int usage, oomleave, check = 0;
+
+       oomleave = memcg_class[memcg_index].oomleave;
+       usage = get_mem_usage(memcg_index);
+       if (usage < oomleave) {
+               _D("%s : usage : %d, oomleave : %d",
+                               __func__, usage, oomleave);
+               check++;
+       }
+       if (oom_usage - total_size < oomleave) {
+               _D("%s : oom_usage : %d, total size : %d, oomleave : %d",
+                               __func__, oom_usage, total_size, oomleave);
+               check++;
+       }
+       return check;
+}
+
+static int lowmem_get_victim_pid(int *pid_arry, unsigned int* pid_size)
+{
+       int count, num_pid = 0;
+       FILE *f;
+       char buf[LOWMEM_PATH_MAX] = {0, };
+
+       f = fopen(VICTIM_TASK, "r");
+
+       if (!f) {
+               _E("Fail to file open");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       /* firstly, read victim count */
+       if (fgets(buf, 32, f) == NULL) {
+               _E("victim list is empty");
+               goto out;
+       }
+
+       count = atoi(buf);
+
+       if (count > MAX_VICTIMS) {
+               _E("Victim count is wrong value");
+               goto out;
+       }
+
+       while (fgets(buf, 32, f) != NULL) {
+               pid_arry[num_pid] = atoi(buf);
+               if (fgets(buf, 32, f) != NULL)
+                       pid_size[num_pid] = atoi(buf);
+               else {
+                       _E("Victim size is needed\n");
+                       goto out;
+               }
+               num_pid++;
+       }
+
+       if (count != num_pid)
+               _I("Number of pids is wrong\n");
+
+       fclose(f);
+       return num_pid;
+out:
+       fclose(f);
+       return RESOURCED_ERROR_FAIL;
+
+
+}
+
+static int lowmem_set_cgroup_leave_threshold(unsigned int value)
+{
+       FILE *f;
+       f = fopen(SET_CGROUP_LEAVE_THRESHOLD, "w");
+
+       if (!f) {
+               _E("Fail to file open");
+               return RESOURCED_ERROR_FAIL;
+       }
+       fprintf(f, "%d", value);
+       fclose(f);
+       return 0;
+}
+
+static int lowmem_set_threshold(void)
+{
+       FILE *f;
+       unsigned int val, total;
+
+       f = fopen(SET_THRESHOLD_RECLAIM, "w");
+
+       if (!f) {
+               _E("Fail to file open : current kernel can't support swap cgroup");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       /* set threshold reclaim */
+       total = _get_total_memory();
+
+       /*
+        * check total memory because total memory is over 1GiB,
+        * we want to start reclaim under 300 MiB remained memory.
+        * But, we check condition 700MiB because reserved memory.
+        */
+       if (total > MBtoB(700))
+               val = MEM_THRESHOLD_RECLAIM;
+       else
+               val = MEM_THRESHOLD_RECLAIM >> 1;
+       fprintf(f, "%d", val);
+       fclose(f);
+
+       /* set threshold level1 */
+       f = fopen(SET_THRESHOLD_LV1, "w");
+
+       if (!f) {
+               _E("Fail to file open");
+               return RESOURCED_ERROR_FAIL;
+       }
+       fprintf(f, "%d", MEM_THRESHOLD_LV1);
+       fclose(f);
+
+       /* set threshold level2 */
+       f = fopen(SET_THRESHOLD_LV2, "w");
+
+       if (!f) {
+               _E("Fail to file open");
+               return RESOURCED_ERROR_FAIL;
+       }
+       fprintf(f, "%d", MEM_THRESHOLD_LV2);
+       fclose(f);
+
+       /* set leave threshold */
+       f = fopen(SET_LEAVE_THRESHOLD, "w");
+
+       if (!f) {
+               _E("Fail to file open");
+               return RESOURCED_ERROR_FAIL;
+       }
+       fprintf(f, "%d", MEM_LEAVE_THRESHOLD);
+       fclose(f);
+       return 0;
+}
+
+void *_lowmem_oom_killer_cb(void *data)
+{
+       int pid, ret, oom_score_adj, count, i;
+       char appname[PROC_NAME_MAX];
+       char popupname[PROC_NAME_MAX];
+       int pid_array[MAX_VICTIMS];
+       unsigned int pid_size[MAX_VICTIMS];
+       unsigned int total_size = 0, forgrd_pid = 0, forgrd_size = 0;
+
+       /* get multiple victims from kernel */
+       count = lowmem_get_victim_pid((int *)pid_array,
+                               (unsigned int *)pid_size);
+
+       if (count < 0) {
+               _E("get victim was failed");
+               return NULL;
+       }
+
+       /* kill all selected process */
+       for (i = 0; i < count; i++) {
+               pid = pid_array[i];
+
+               if (pid <= 0)
+                       continue;
+               _D("oom total memory size : %d", total_size);
+               ret = proc_get_cmdline(pid, appname);
+               if (ret != 0) {
+                       _D("invalid pid(%d) was selected", pid);
+                       continue;
+               }
+               if (!strcmp("memps", appname)) {
+                       _E("memps(%d) was selected, skip it", pid);
+                       continue;
+               }
+               if (!strcmp("crash-worker", appname)) {
+                       _E("crash-worker(%d) was selected, skip it", pid);
+                       continue;
+               }
+
+               /* make memps log for killing application firstly */
+               if (i == 0)
+                       make_memps_log(MEMPS_LOG_FILE, pid, appname);
+
+               if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
+                       _D("pid(%d) was already terminated", pid);
+                       continue;
+               }
+
+               /* just continue if selected process moved to foreground */
+               if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && oom_score_adj < OOMADJ_BACKGRD_UNLOCKED)
+                       continue;
+
+               proc_remove_process_list(pid);
+               kill(pid, SIGKILL);
+
+               total_size += pid_size[i];
+               _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
+                               pid, appname, oom_score_adj);
+
+               /* wait OOM_MULTIKILL_WAIT for removing a latency about killing proesss */
+               if (BtoMB(total_size) > MEM_LEAVE_THRESHOLD && i%5==0)
+                       usleep(OOM_MULTIKILL_WAIT);
+
+               if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
+                       continue;
+
+               if (forgrd_size < pid_size[i]) {
+                       forgrd_pid = pid;
+                       forgrd_size = pid_size[i];
+                       strncpy(popupname, appname, PROC_NAME_MAX-1);
+               }
+       }
+
+       if (forgrd_pid)
+               make_memps_log(MEMPS_LOG_FILE, forgrd_pid, popupname);
+
+       return NULL;
+}
+
+int lowmem_oom_killer_cb(int memcg_idx, int flags)
+{
+       int memcg_index = memcg_idx;
+       _lowmem_oom_killer_cb((void *)&memcg_index);
+       return 0;
+}
+
+static void lowmem_cgroup_oom_killer(int memcg_index)
+{
+       int pid, ret, oom_score_adj, count, i;
+       char appname[PATH_MAX];
+       int pid_array[32];
+       unsigned int pid_size[32];
+       unsigned int total_size = 0, oom_usage = 0;
+
+       oom_usage = get_mem_usage(memcg_index);
+       /* get multiple victims from kernel */
+       count = lowmem_get_victim_pid((int *)pid_array,
+                               (unsigned int *)pid_size);
+
+       if (count < 0) {
+               _E("get victim was failed");
+               return;
+       }
+
+       for (i = 0; i < count; i++) {
+               pid = pid_array[i];
+
+               if (pid <= 0)
+                       continue;
+               _D("oom total memory size : %d", total_size);
+               ret = proc_get_cmdline(pid, appname);
+               if (ret != 0) {
+                       _E("invalid pid(%d) was selected", pid);
+                       continue;
+               }
+               if (!strcmp("memps", appname)) {
+                       _E("memps(%d) was selected, skip it", pid);
+                       continue;
+               }
+               if (!strcmp("crash-worker", appname)) {
+                       _E("crash-worker(%d) was selected, skip it", pid);
+                       continue;
+               }
+               if (proc_get_oom_score_adj(pid, &oom_score_adj) < 0) {
+                       _D("pid(%d) was already terminated", pid);
+                       continue;
+               }
+
+               /* check current memory status */
+               if (lowmem_check_current_state(memcg_index, total_size,
+                                       oom_usage) > 0)
+                       return;
+
+               /* make memps log for killing application firstly */
+               if (i==0)
+                       make_memps_log(MEMPS_LOG_FILE, pid, appname);
+
+               proc_remove_process_list(pid);
+               kill(pid, SIGTERM);
+
+               total_size += pid_size[i];
+               _I("we killed, lowmem lv2 = %d (%s) oom = %d\n",
+                               pid, appname, oom_score_adj);
+
+               if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
+                       continue;
+
+               if (i != 0)
+                       make_memps_log(MEMPS_LOG_FILE, pid, appname);
+       }
+}
+
+static char *convert_to_str(unsigned int mem_state)
+{
+       char *tmp = NULL;
+       switch (mem_state) {
+       case MEMNOTIFY_NORMAL:
+       case MEMNOTIFY_RECLAIM:
+               tmp = "mem normal";
+               break;
+       case MEMNOTIFY_LOW:
+               tmp = "mem low";
+               break;
+       case MEMNOTIFY_CRITICAL:
+               tmp = "mem critical";
+               break;
+       default:
+               assert(0);
+       }
+       return tmp;
+}
+
+static void print_lowmem_state(unsigned int mem_state)
+{
+       _I("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
+               convert_to_str(mem_state));
+}
+
+static void lowmem_swap_memory(void)
+{
+       pid_t pid;
+       int swap_type;
+
+       if (cur_mem_state == MEMNOTIFY_NORMAL)
+               return;
+
+       swap_type = swap_status(SWAP_GET_TYPE, NULL);
+
+       if (swap_type == SWAP_ON) {
+               while (1)
+               {
+                       pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL);
+                       if (!pid)
+                               break;
+                       _I("swap cgroup entered : pid : %d", (int)pid);
+                       resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid);
+               }
+               if (swap_status(SWAP_GET_STATUS, NULL) == SWAP_OFF)
+                       resourced_notify(RESOURCED_NOTIFIER_SWAP_RESTART, NULL);
+               resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL);
+       }
+}
+
+static int memory_reclaim_act(void *data)
+{
+       int ret, status;
+       _I("[LOW MEM STATE] memory reclaim state");
+       ret = vconf_get_int(VCONFKEY_SYSMAN_LOW_MEMORY, &status);
+       if (ret != 0) {
+               _E("vconf get failed(VCONFKEY_SYSMAN_LOW_MEMORY)\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+       if (status != VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL)
+               vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+                                 VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+       else
+               lowmem_swap_memory();
+
+       return 0;
+}
+
+static int memory_low_act(void *data)
+{
+       _I("[LOW MEM STATE] memory low state");
+       print_mem_state();
+       remove_shm();
+
+       vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+                     VCONFKEY_SYSMAN_LOW_MEMORY_SOFT_WARNING);
+
+       return 0;
+}
+
+static int memory_oom_act(void *ad)
+{
+       pthread_t pth;
+       int ret;
+
+       _I("[LOW MEM STATE] memory oom state");
+
+       print_mem_state();
+
+       ret = pthread_create(&pth, 0, _lowmem_oom_killer_cb, (void*)NULL);
+       if (ret < 0) {
+               _E("pthread creation failed!, call directly!");
+               _lowmem_oom_killer_cb((void*)NULL);
+       } else
+               pthread_detach(pth);
+
+       vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+                     VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
+       return 1;
+}
+
+static int memory_cgroup_oom_act(int memcg_index)
+{
+       _I("[LOW MEM STATE] memory oom state");
+
+       print_mem_state();
+
+       lowmem_cgroup_oom_killer(memcg_index);
+
+       vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+                     VCONFKEY_SYSMAN_LOW_MEMORY_HARD_WARNING);
+       return 1;
+}
+
+static int memory_normal_act(void *data)
+{
+       _I("[LOW MEM STATE] memory normal state");
+       vconf_set_int(VCONFKEY_SYSMAN_LOW_MEMORY,
+                     VCONFKEY_SYSMAN_LOW_MEMORY_NORMAL);
+       return 0;
+}
+
+static int lowmem_process(unsigned int mem_state, void *ad)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(lpe); i++) {
+               if ((cur_mem_state == lpe[i].cur_mem_state)
+                               && (mem_state == lpe[i].new_mem_state)) {
+                       if (oom_check_timer != NULL) {
+                               ecore_timer_del(oom_check_timer);
+                               oom_check_timer = NULL;
+                       }
+                       cur_mem_state = mem_state;
+                       lpe[i].action(ad);
+                       if (mem_state == MEMNOTIFY_CRITICAL)
+                               oom_check_timer =
+                                       ecore_timer_add(OOM_TIMER_INTERVAL, (const void *)lpe[i].action, ad);
+                       return 0;
+               }
+       }
+       cur_mem_state = mem_state;
+       return 0;
+}
+
+static unsigned int lowmem_eventfd_read(int fd)
+{
+       unsigned int ret;
+       uint64_t dummy_state;
+       ret = read(fd, &dummy_state, sizeof(dummy_state));
+       return ret;
+}
+
+static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler)
+{
+       int fd, i, currentoom;
+       struct ss_main_data *ad = (struct ss_main_data *)data;
+
+       if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
+               _E("ecore_main_fd_handler_active_get error , return\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       fd = ecore_main_fd_handler_fd_get(fd_handler);
+       if (fd < 0) {
+               _E("ecore_main_fd_handler_fd_get error , return\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+       lowmem_eventfd_read(fd);
+
+       for (i = 0; i < MEMCG_GROUP_MAX; i++) {
+               currentoom = get_current_oom(i);
+               if (currentoom == MEMGC_OOM_NORMAL) {
+                       if (memcg_class[i].oomalert)
+                               memory_normal_act(ad);
+               }
+               if (currentoom > memcg_class[i].oomlevel) {
+                       switch (currentoom) {
+                       case MEMGC_OOM_WARNING:
+                               memory_low_act(ad);
+                               break;
+                       case MEMGC_OOM_HIGH:
+                               memcg_class[i].oomalert = 1;
+                               memory_cgroup_oom_act(i);
+                               break;
+                       case MEMGC_OOM_CRITICAL:
+                               memcg_class[i].oomalert = 1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               memcg_class[i].oomlevel = currentoom;
+       }
+
+       return ECORE_CALLBACK_RENEW;
+}
+
+/*
+From memory.txt kernel document -
+To register a notifier, application need:
+- create an eventfd using eventfd(2)
+- open memory.oom_control file
+- write string like "<event_fd> <fd of memory.oom_control>"
+to cgroup.event_control
+*/
+
+static int setup_eventfd(void)
+{
+       unsigned int thres, i;
+       int mcgfd, cgfd, evfd, res, sz, ret = -1;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       int buflen = sizeof(buf);
+
+       /* create an eventfd using eventfd(2)
+       use same event fd for using ecore event loop */
+       evfd = eventfd(0, 0);
+       ret = fcntl(evfd, F_SETFL, O_NONBLOCK);
+       if (ret < 0)
+               return RESOURCED_ERROR_FAIL;
+
+       for (i = 0; i < MEMCG_GROUP_MAX; i++) {
+               /* open cgroup.event_control */
+               snprintf(buf, buflen, "%s/%s/cgroup.event_control",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               cgfd = open(buf, O_WRONLY);
+               if (cgfd < 0) {
+                       _E("open event_control failed");
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* register event in usage_in_byte */
+               snprintf(buf, buflen, "%s/%s/memory.usage_in_bytes",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               mcgfd = open(buf, O_RDONLY);
+               if (mcgfd < 0) {
+                       _E("open memory control failed");
+                       close(cgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* threshold lv 1 : wakeup softswapd */
+               /* write event fd about threshold lv1 */
+               thres = memcg_class[i].thres_lv1;
+               sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
+               sz += 1;
+               res = write(cgfd, buf, sz);
+               if (res != sz) {
+                       _E("write cgfd failed : %d", res);
+                       close(cgfd);
+                       close(mcgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* calculate threshold lv_2 */
+               /* threshold lv 2 : lowmem warning */
+               thres = memcg_class[i].thres_lv2;
+
+               /* write event fd about threshold lv1 */
+               sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
+               sz += 1;
+               res = write(cgfd, buf, sz);
+               if (res != sz) {
+                       _E("write cgfd failed : %d", res);
+                       close(cgfd);
+                       close(mcgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* calculate threshold lv_3 */
+               /* threshold lv 3 : victim kill */
+               thres = memcg_class[i].thres_lv3;
+
+               /* write event fd about threshold lv2 */
+               sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
+               sz += 1;
+               res = write(cgfd, buf, sz);
+               if (res != sz) {
+                       _E("write cgfd failed : %d", res);
+                       close(cgfd);
+                       close(mcgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+               close(mcgfd);
+
+               /* register event in oom_control */
+               snprintf(buf, buflen, "%s/%s/memory.oom_control",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+
+               mcgfd = open(buf, O_RDONLY);
+               if (mcgfd < 0) {
+                       _E("open memory control failed");
+                       close(cgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* write event fd about oom control with zero threshold*/
+               thres = 0;
+               sz = snprintf(buf, buflen, "%d %d %d", evfd, mcgfd, thres);
+               sz += 1;
+               res = write(cgfd, buf, sz);
+               if (res != sz) {
+                       _E("write cgfd failed : %d", res);
+                       close(cgfd);
+                       close(mcgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+               close(cgfd);
+               close(mcgfd);
+       }
+       return evfd;
+}
+
+void set_threshold(int level, int thres)
+{
+       return;
+}
+
+void set_leave_threshold(int thres)
+{
+       return;
+}
+
+static int init_memcg(void)
+{
+       unsigned int total, i, limit, size;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       FILE *f;
+       total = _get_total_memory();
+       _D("Total : %d", total);
+
+       for (i = 0; i < MEMCG_GROUP_MAX; i++) {
+               /* write limit_in_bytes */
+               snprintf(buf, sizeof(buf), "%s/%s/memory.limit_in_bytes",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               _D("buf : %s", buf);
+               f = fopen(buf, "w");
+               if (!f) {
+                       _E("%s open failed", buf);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               limit = (unsigned int)(memcg_class[i].limit_ratio*(float)total);
+
+               if (limit > memcg_class[i].min_limit)
+                       limit = memcg_class[i].min_limit;
+
+               size = snprintf(buf, sizeof(buf), "%u", limit);
+               if (fwrite(buf, size, 1, f) != 1)
+                       _E("fwrite memory.limit_in_bytes : %d\n", limit);
+               fclose(f);
+
+               /* save memory limitation for calculating threshold */
+               memcg_class[i].total_limit = limit;
+
+               _calc_threshold(i, limit);
+
+               /* set leave threshold value to kernel */
+               lowmem_set_cgroup_leave_threshold(memcg_class[i].oomleave);
+
+               /* enable cgroup move */
+               snprintf(buf, sizeof(buf), "%s/%s/memory.move_charge_at_immigrate",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               _D("buf : %s", buf);
+               f = fopen(buf, "w");
+               if (!f) {
+                       _E("%s open failed", buf);
+                       return RESOURCED_ERROR_FAIL;
+               }
+               size = snprintf(buf, sizeof(buf), "3");
+               if (fwrite(buf, size, 1, f) != 1)
+                       _E("fwrite memory.move_charge_at_immigrate\n");
+               fclose(f);
+
+       }
+       return 0;
+}
+
+static void lowmem_move_memcgroup(int pid, int oom_score_adj)
+{
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       FILE *f;
+       int size, background = 0;
+       unsigned long swap_args[1] = {0,};
+
+       if (oom_score_adj > OOMADJ_BACKGRD_LOCKED) {
+               snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
+               background = 1;
+       }
+       else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED &&
+                                       oom_score_adj < OOMADJ_BACKGRD_LOCKED)
+               snprintf(buf, sizeof(buf), "%s/foreground/cgroup.procs", MEMCG_PATH);
+       else
+               return;
+
+       swap_args[0] = (unsigned long)pid;
+       if (!swap_status(SWAP_CHECK_PID, swap_args) || !background) {
+               _I("buf : %s, pid : %d, oom : %d", buf, pid, oom_score_adj);
+               f = fopen(buf, "w");
+               if (!f) {
+                       _E("%s open failed", buf);
+                       return;
+               }
+               size = snprintf(buf, sizeof(buf), "%d", pid);
+               if (fwrite(buf, size, 1, f) != 1)
+                       _E("fwrite cgroup tasks : %d\n", pid);
+               fclose(f);
+       }
+       if (background)
+               lowmem_swap_memory();
+}
+
+static void lowmem_cgroup_foregrd_manage(int currentpid)
+{
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       int pid, pgid;
+       FILE *f;
+       snprintf(buf, sizeof(buf), "%s/background/cgroup.procs", MEMCG_PATH);
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed", buf);
+               return;
+       }
+       while (fgets(buf, LOWMEM_PATH_MAX, f) != NULL) {
+               pid = atoi(buf);
+               if (currentpid == pid)
+                       continue;
+               pgid = getpgid(pid);
+               if (currentpid == pgid)
+                       lowmem_move_memcgroup(pid, OOMADJ_APP_LIMIT);
+       }
+       fclose(f);
+}
+
+static unsigned int lowmem_read(int fd)
+{
+       unsigned int mem_state;
+       if (read(fd, &mem_state, sizeof(mem_state)) < 0) {
+               _E("error lowmem state");
+               return RESOURCED_ERROR_FAIL;
+       }
+       return mem_state;
+}
+
+static Eina_Bool lowmem_efd_cb(void *data, Ecore_Fd_Handler *fd_handler)
+{
+       int fd;
+       struct ss_main_data *ad = (struct ss_main_data *)data;
+       unsigned int mem_state;
+
+       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;
+       }
+
+       mem_state = lowmem_read(fd);
+       if (mem_state == -1) {
+               lowmem_fd_stop(fd);
+               _E("error lowmem_read, restart lowmem fd");
+               lowmem_fd_start();
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       print_lowmem_state(mem_state);
+       lowmem_process(mem_state, ad);
+
+       return ECORE_CALLBACK_RENEW;
+}
+
+static int lowmem_fd_start(void)
+{
+       lowmem_fd = open("/dev/lowmemnotify", O_RDONLY);
+       if (lowmem_fd < 0) {
+               _E("lowmem_fd_start fd open failed");
+               return RESOURCED_ERROR_FAIL;
+       } else
+               _D("lowmem_fd_start open /dev/lowmemnotify sucess\n");
+
+       oom_check_timer = NULL;
+       lowmem_efd = ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
+                                              (Ecore_Fd_Cb)lowmem_efd_cb, NULL,
+                                              NULL, NULL);
+       if (!lowmem_efd) {
+               _E("error ecore_main_fd_handler_add in lowmem_fd_start\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+       return 0;
+}
+
+int lowmem_init(void)
+{
+       int ret = RESOURCED_ERROR_NONE;
+
+       /* set default memcg value */
+       ret = init_memcg();
+       if (ret < 0) {
+               _E("memory cgroup init failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       ret = lowmem_fd_start();
+       if (ret < 0) {
+               _E("lowmem_fd_start fail\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       /* set threshold level 1, level 2, leave threshold */
+       ret = lowmem_set_threshold();
+       if (ret < 0) {
+               _E("lowmem_set_threshold fail\n");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       /* register threshold and event fd */
+       lowmem_fd = setup_eventfd();
+       if (lowmem_fd < 0) {
+               _E("setup event fd is failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       ecore_main_fd_handler_add(lowmem_fd, ECORE_FD_READ,
+                                 (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL);
+
+       _I("lowmem_swaptype : %d", swap_status(SWAP_GET_TYPE, NULL));
+
+       lowmem_dbus_init();
+
+       return 0;
+}
+
+static int lowmem_fd_stop(int fd)
+{
+       if (lowmem_efd) {
+               ecore_main_fd_handler_del(lowmem_efd);
+               lowmem_efd = NULL;
+       }
+       if (fd >= 0) {
+               close(fd);
+               fd = -1;
+       }
+       return 0;
+}
+
+static int resourced_memory_control(void *data)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct lowmem_data_type *l_data;
+
+       l_data = (struct lowmem_data_type *)data;
+       switch(l_data->control_type) {
+       case LOWMEM_MOVE_CGROUP:
+               if (l_data->args)
+                       lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]);
+               break;
+       case LOWMEM_MANAGE_FOREGROUND:
+               if (l_data->args)
+                       lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]);
+               break;
+
+       }
+       return ret;
+}
+
+static int resourced_memory_init(void *data)
+{
+       lowmem_ops = &memory_modules_ops;
+
+       return lowmem_init();
+}
+
+static int resourced_memory_finalize(void *data)
+{
+       return RESOURCED_ERROR_NONE;
+}
+
+int lowmem_control(enum lowmem_control_type type, unsigned long *args)
+{
+       struct lowmem_data_type l_data;
+
+       if (lowmem_ops) {
+               l_data.control_type = type;
+               l_data.args = args;
+               return lowmem_ops->control(&l_data);
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops memory_modules_ops = {
+       .priority       = MODULE_PRIORITY_NORMAL,
+       .name           = "lowmem",
+       .init           = resourced_memory_init,
+       .exit           = resourced_memory_finalize,
+       .control        = resourced_memory_control,
+};
+
+MODULE_REGISTER(&memory_modules_ops)
diff --git a/src/memory/lowmem-handler.h b/src/memory/lowmem-handler.h
new file mode 100644 (file)
index 0000000..d938ddd
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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__
+
+void lowmem_dbus_init(void);
+int lowmem_oom_killer_cb(int memcg_idx, int flags);
+void lowmem_dynamic_process_killer(int type);
+unsigned int get_available(void);
+void change_memory_state(int state, int force);
+
+void set_threshold(int level, int thres);
+void set_leave_threshold(int thres);
+
+#define NUM_FOREGROUND                 3
+enum {
+       MEMCG_MEMORY,
+       MEMCG_FOREGROUND,
+       MEMCG_BACKGROUND = MEMCG_FOREGROUND + NUM_FOREGROUND,
+       MEMCG_MAX_GROUPS,
+};
+
+enum {
+       MEMNOTIFY_NORMAL,
+       MEMNOTIFY_SWAP,
+       MEMNOTIFY_LOW,
+       MEMNOTIFY_MEDIUM,
+       MEMNOTIFY_MAX_LEVELS,
+};
+
+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 */
+};
+
+enum {
+       DYNAMIC_KILL_LARGEHEAP,
+       DYNAMIC_KILL_LUNCH,
+       DYNAMIC_KILL_MAX,
+};
+
+#endif /*__LOWMEM_HANDLER_H__*/
diff --git a/src/memory/memory_eng.conf b/src/memory/memory_eng.conf
new file mode 100644 (file)
index 0000000..81751d2
--- /dev/null
@@ -0,0 +1,103 @@
+[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
+
+[Memory512]
+# Threshold to start swap
+ThresholdSwap=100              # MB
+
+# Threshold to start reclaim
+ThresholdLow=60                        # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=40             # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=70              # 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=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
diff --git a/src/memory/memory_user.conf b/src/memory/memory_user.conf
new file mode 100644 (file)
index 0000000..f821ba2
--- /dev/null
@@ -0,0 +1,100 @@
+[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
+
+[Memory512]
+# Threshold to start swap
+ThresholdSwap=100              # MB
+
+# Threshold to start reclaim
+ThresholdLow=60                        # MB
+
+# Threshold to start low memory killer
+ThresholdMedium=40             # MB
+
+# Threshold to stop low memory killer
+ThresholdLeave=70              # MB
+
+# Foreground limit ratio
+ForegroundRatio=1
+
+# 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
diff --git a/src/memory/vmpressure-lowmem-handler.c b/src/memory/vmpressure-lowmem-handler.c
new file mode 100644 (file)
index 0000000..8efe615
--- /dev/null
@@ -0,0 +1,1975 @@
+/*
+ * 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_handler.c
+ *
+ * @desc lowmem handler using memcgroup
+ *
+ * Copyright (c) 2013 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 "trace.h"
+#include "cgroup.h"
+#include "proc-main.h"
+#include "lowmem-handler.h"
+#include "proc-process.h"
+#include "swap-common.h"
+#include "lowmem-common.h"
+#include "resourced.h"
+#include "macro.h"
+#include "notifier.h"
+#include "config-parser.h"
+#include "module.h"
+#include "logging-common.h"
+
+#define MEMINFO_PATH                   "/proc/meminfo"
+#define MEMCG_PATH                     "/sys/fs/cgroup"
+#define MEMPS_LOG_PATH                 "/var/log/"
+#define MEMPS_LOG_FILE                 MEMPS_LOG_PATH"memps"
+#define MEMPS_EXEC_PATH                        "usr/bin/memps"
+#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 MEM_CONF_FILE                   "/etc/resourced/memory.conf"
+#define MEM_CONF_SECTION                "VIP_PROCESS"
+#define MEM_CONF_PREDEFINE              "PREDEFINE"
+
+#define BtoMB(x)                       ((x) >> 20)
+#define BtoKB(x)                       ((x) >> 10)
+#define BtoPAGE(x)                     ((x) >> 12)
+
+#define NO_LIMIT                       -1
+/* for memory cgroup, set no limit */
+#define MEMCG_MEMORY_LIMIT_RATIO       NO_LIMIT
+#define MEMCG_FOREGROUND_LIMIT_RATIO   1
+/* for background cgroup, set no limit */
+#define MEMCG_BACKGROUND_LIMIT_RATIO   NO_LIMIT
+#define MEMCG_FOREGROUND_MIN_LIMIT     UINT_MAX
+#define MEMCG_BACKGROUND_MIN_LIMIT     UINT_MAX
+#define MEMCG_LOW_RATIO                        0.8
+#define MEMCG_MEDIUM_RATIO             0.96
+#define MEMCG_FOREGROUND_THRES_LEAVE   100 /* MB */
+#define MEMCG_FOREGROUND_LEAVE_RATIO   0.25
+
+#define BUF_MAX                                1024
+#define LOWMEM_PATH_MAX                        100
+#define MAX_MEMORY_CGROUP_VICTIMS      10
+#define MAX_CGROUP_VICTIMS             1
+#define OOM_TIMER_INTERVAL             2
+#define OOM_MULTIKILL_WAIT             (500*1000)
+#define OOM_SIGKILL_WAIT               1
+#define OOM_SCORE_POINT_WEIGHT         1500
+#define OOM_KILLER_PRIORITY            -20
+#define MAX_FD_VICTIMS                 10
+#define MAX_SWAP_VICTIMS               5
+#define MAX_MEMPS_LOGS                 50
+#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_512                   512 /* MB */
+#define MEM_SIZE_1024                  1024 /* MB */
+#define MEM_SIZE_2048                  2048 /* MB */
+
+/* thresholds for 64M RAM*/
+#define DYNAMIC_PROCESS_64_THRES               10 /* MB */
+#define DYNAMIC_PROCESS_64_LEAVE               30 /* 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 DYNAMIC_PROCESS_256_THRES              50 /* MB */
+#define DYNAMIC_PROCESS_256_LEAVE              80 /* 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 512M RAM */
+#define DYNAMIC_PROCESS_512_THRES              80 /* MB */
+#define DYNAMIC_PROCESS_512_LEAVE              100 /* MB */
+#define DYNAMIC_PROCESS_512_THRESLAUNCH        60 /* MB */
+#define DYNAMIC_PROCESS_512_LEAVELAUNCH        80 /* MB */
+#define 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 more than 1024M RAM */
+#define DYNAMIC_PROCESS_1024_THRES             150 /* MB */
+#define DYNAMIC_PROCESS_1024_LEAVE             300 /* 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 DYNAMIC_PROCESS_2048_THRES             200 /* MB */
+#define DYNAMIC_PROCESS_2048_LEAVE             500 /* 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 int thresholds[MEMNOTIFY_MAX_LEVELS];
+static int dynamic_process_threshold[DYNAMIC_KILL_MAX];
+static int dynamic_process_leave[DYNAMIC_KILL_MAX];
+
+struct task_info {
+       pid_t pid;
+       pid_t pgid;
+       int oom_score_adj;
+       int size;
+};
+
+struct memcg_class {
+       unsigned int min_limit;  /* minimum limit */
+       /* limit ratio, if don't want to set limit, use NO_LIMIT*/
+       float limit_ratio;
+       unsigned int oomleave;  /* leave memory usage */
+       char *cgroup_name;      /* cgroup name */
+       unsigned int thres_low; /* low level threshold */
+       unsigned int thres_medium; /* medium level threshold */
+       unsigned int thres_leave;  /* leave threshold */
+       /* vmpressure event string. If don't want to register event, use null */
+       char *event_string;
+       /* compare function for selecting victims in each cgroup */
+       int (*compare_fn) (const struct task_info *, const struct task_info *);
+};
+
+struct lowmem_process_entry {
+       int cur_mem_state;
+       int new_mem_state;
+       void (*action) (void);
+};
+
+
+struct victims {
+       int num;
+       pid_t pids[MAX_MEMORY_CGROUP_VICTIMS];
+};
+
+/* low memory action function for cgroup */
+static void memory_cgroup_medium_act(int memcg_idx);
+static int compare_mem_victims(const struct task_info *ta, const struct task_info *tb);
+static int compare_bg_victims(const struct task_info *ta, const struct task_info *tb);
+static int compare_fg_victims(const struct task_info *ta, const struct task_info *tb);
+/* 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)                \
+       { MEMNOTIFY_##c, MEMNOTIFY_##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 struct memcg_class memcg_class[MEMCG_MAX_GROUPS] = {
+       {NO_LIMIT,                      MEMCG_MEMORY_LIMIT_RATIO,
+       0,                              "memory",
+       0,                              0,
+       0,                              "medium", /* register medium event*/
+       compare_mem_victims},
+       {MEMCG_FOREGROUND_MIN_LIMIT,    MEMCG_FOREGROUND_LIMIT_RATIO,
+       0,                              "memory/foreground1",
+       0,                              0,
+       MEMCG_FOREGROUND_THRES_LEAVE,   "medium",
+       compare_fg_victims},
+       {MEMCG_FOREGROUND_MIN_LIMIT,    MEMCG_FOREGROUND_LIMIT_RATIO,
+       0,                              "memory/foreground2",
+       0,                              0,
+       MEMCG_FOREGROUND_THRES_LEAVE,   "medium",
+       compare_fg_victims},
+       {MEMCG_FOREGROUND_MIN_LIMIT,    MEMCG_FOREGROUND_LIMIT_RATIO,
+       0,                              "memory/foreground3",
+       0,                              0,
+       MEMCG_FOREGROUND_THRES_LEAVE,   "medium",
+       compare_fg_victims},
+       {MEMCG_BACKGROUND_MIN_LIMIT,    MEMCG_BACKGROUND_LIMIT_RATIO,
+       0,                              "memory/background",
+       0,                              0,
+       0,                              NULL, /* register no event*/
+       compare_bg_victims},
+};
+
+static int evfd[MEMCG_MAX_GROUPS] = {-1, };
+static int cur_mem_state = MEMNOTIFY_NORMAL;
+static Ecore_Timer *oom_check_timer = NULL;
+static Ecore_Timer *oom_sigkill_timer = NULL;
+static pid_t killed_fg_victim;
+static int num_max_victims = MAX_MEMORY_CGROUP_VICTIMS;
+struct victims killed_tasks = {0, {0, }};
+
+static pthread_t       oom_thread      = 0;
+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 const struct module_ops memory_modules_ops;
+static const struct module_ops *lowmem_ops;
+
+static inline void get_total_memory(void)
+{
+       struct sysinfo si;
+       if (totalram)
+               return;
+
+       if (!sysinfo(&si)) {
+               totalram = si.totalram;
+               ktotalram = BtoKB(totalram);
+       }
+}
+
+unsigned int get_available(void)
+{
+       char buf[PATH_MAX];
+       FILE *fp;
+       char *idx;
+       unsigned int free = 0, cached = 0;
+       unsigned int available = 0;
+
+       fp = fopen(MEMINFO_PATH, "r");
+
+       if (!fp) {
+               _E("%s open failed, %d", buf, fp);
+               return available;
+       }
+
+       while (fgets(buf, PATH_MAX, fp) != NULL) {
+               if ((idx = strstr(buf, "MemFree:"))) {
+                       idx += strlen("MemFree:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       free = atoi(idx);
+               } else if ((idx = strstr(buf, "MemAvailable:"))) {
+                       idx += strlen("MemAvailable:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       available = atoi(idx);
+                       break;
+               } else if((idx = strstr(buf, "Cached:"))) {
+                       idx += strlen("Cached:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       cached = atoi(idx);
+                       break;
+               }
+       }
+
+       if (available == 0)
+               available = free + cached;
+       available >>= 10;
+       fclose(fp);
+
+       return available;
+}
+
+static bool get_mem_usage_by_pid(pid_t pid, unsigned int *rss)
+{
+       FILE *fp;
+       char proc_path[PATH_MAX];
+
+       snprintf(proc_path, sizeof(proc_path), "/proc/%d/statm", pid);
+       fp = fopen(proc_path, "r");
+       if (fp == NULL)
+               return false;
+
+       if (fscanf(fp, "%*s %d", rss) < 1) {
+               fclose(fp);
+               return false;
+       }
+
+       fclose(fp);
+
+       /* convert page to Kb */
+       *rss *= 4;
+       return true;
+}
+
+static unsigned int get_mem_usage(int idx)
+{
+       FILE *f;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       unsigned int usage;
+
+       snprintf(buf, sizeof(buf), "%s/%s/memory.usage_in_bytes",
+                       MEMCG_PATH, memcg_class[idx].cgroup_name);
+
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed, %d", buf, f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       if (fgets(buf, 32, f) == NULL) {
+               _E("fgets failed\n");
+               fclose(f);
+               return RESOURCED_ERROR_FAIL;
+       }
+       usage = atoi(buf);
+       fclose(f);
+
+       return usage;
+}
+
+static int get_mem_usage_anon(int idx, unsigned int *result)
+{
+       FILE *f;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       char line[BUF_MAX] = {0, };
+       char name[30] = {0, };
+       unsigned int tmp, active_anon = 0, inactive_anon = 0;
+
+       snprintf(buf, sizeof(buf), "%s/%s/memory.stat",
+                       MEMCG_PATH, memcg_class[idx].cgroup_name);
+
+       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);
+       *result = active_anon + inactive_anon;
+
+       return RESOURCED_ERROR_NONE;
+}
+
+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", dir);
+                       strcat(fname, 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", new_log, (char *)NULL);
+               exit(0);
+       }
+}
+
+static int lowmem_check_current_state(int memcg_index)
+{
+       unsigned int usage, oomleave;
+       int ret;
+
+       oomleave = memcg_class[memcg_index].oomleave;
+       ret = get_mem_usage_anon(memcg_index, &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 compare_mem_victims(const struct task_info *ta, const struct task_info *tb)
+{
+       int pa, pb;
+       assert(ta != NULL);
+       assert(tb != NULL);
+
+       /*
+        * Weight task size ratio to totalram by OOM_SCORE_POINT_WEIGHT so that
+        * tasks with score -1000 or -900 could be selected as victims if they consumes
+        * memory more than 70% of totalram.
+        */
+       pa = (int)(ta->size * OOM_SCORE_POINT_WEIGHT) / ktotalram + ta->oom_score_adj;
+       pb = (int)(tb->size * OOM_SCORE_POINT_WEIGHT) / ktotalram + tb->oom_score_adj;
+
+       return (pb - pa);
+}
+
+static int compare_bg_victims(const struct task_info *ta, const struct task_info *tb)
+{
+       /*
+       * Firstly, sort by oom_score_adj
+       * Secondly, sort by task size
+       */
+       assert(ta != NULL);
+       assert(tb != NULL);
+
+       if (ta->oom_score_adj != tb->oom_score_adj)
+               return (tb->oom_score_adj - ta->oom_score_adj);
+
+       return ((int)(tb->size) - (int)(ta->size));
+}
+
+static int compare_fg_victims(const struct task_info *ta, const struct task_info *tb)
+{
+       /*
+       * only sort by task size
+       */
+       assert(ta != NULL);
+       assert(tb != NULL);
+
+       return ((int)(tb->size) - (int)(ta->size));
+}
+
+static int lowmem_get_cgroup_victims(int idx, int max_victims, struct task_info *selected,
+       unsigned should_be_freed, int flags)
+{
+       FILE *f = NULL;
+       char buf[LOWMEM_PATH_MAX] = {0, };
+       int i = 0;
+       int num_victims = 0;
+       unsigned total_victim_size = 0;
+       char appname[PATH_MAX] = {0, };
+
+       GArray *victim_candidates = NULL;
+
+       victim_candidates = g_array_new(false, false, sizeof(struct task_info));
+
+       /* if g_array_new fails, return the current number of victims */
+       if (victim_candidates == NULL)
+               return num_victims;
+
+       if (idx == MEMCG_MEMORY) {
+               snprintf(buf, sizeof(buf), "%s/%s/system.slice/cgroup.procs",
+                               MEMCG_PATH, memcg_class[idx].cgroup_name);
+               f = fopen(buf, "r");
+       }
+
+       if (!f) {
+               snprintf(buf, sizeof(buf), "%s/%s/cgroup.procs",
+                       MEMCG_PATH, memcg_class[idx].cgroup_name);
+
+               f = fopen(buf, "r");
+               if (!f) {
+                       _E("%s open failed, %d", buf, f);
+                       /*
+                        * if task read in this cgroup fails,
+                        * return the current number of victims
+                        */
+                       return num_victims;
+               }
+       }
+
+       while (fgets(buf, 32, f) != NULL) {
+               struct task_info new_victim;
+               pid_t tpid = 0;
+               int toom = 0;
+               unsigned int tsize = 0;
+
+               tpid = atoi(buf);
+
+               if (proc_get_oom_score_adj(tpid, &toom) < 0) {
+                       _D("pid(%d) was already terminated", tpid);
+                       continue;
+               }
+
+               if (!get_mem_usage_by_pid(tpid, &tsize)) {
+                       _D("pid(%d) size is not available\n", tpid);
+                       continue;
+               }
+
+               if(proc_get_cmdline(tpid, appname) == RESOURCED_ERROR_FAIL)
+                       continue;
+
+               for (i = 0; i < victim_candidates->len; i++) {
+                       struct task_info *tsk = &g_array_index(victim_candidates,
+                                                       struct task_info, i);
+                       if (getpgid(tpid) == tsk->pgid) {
+                               tsk->size += tsize;
+                               if (tsk->oom_score_adj <= 0 && toom > 0) {
+                                       tsk->pid = tpid;
+                                       tsk->oom_score_adj = toom;
+                               }
+                               break;
+                       }
+               }
+
+               if (i == victim_candidates->len) {
+                       new_victim.pid = tpid;
+                       new_victim.pgid = getpgid(tpid);
+                       new_victim.oom_score_adj = toom;
+                       new_victim.size = tsize;
+
+                       g_array_append_val(victim_candidates, new_victim);
+               }
+       }
+
+       /*
+        * if there is no tasks in this cgroup,
+        * return the current number of victims
+        */
+       if (victim_candidates->len == 0) {
+               g_array_free(victim_candidates, true);
+               fclose(f);
+               return num_victims;
+       }
+
+       g_array_sort(victim_candidates,
+                       (GCompareFunc)memcg_class[idx].compare_fn);
+
+       for (i = 0; i < victim_candidates->len; i++) {
+               struct task_info tsk;
+               if (num_victims >= max_victims ||
+                   (!(flags & OOM_NOMEMORY_CHECK) &&
+                       total_victim_size >= should_be_freed)) {
+                       break;
+               }
+
+               tsk = g_array_index(victim_candidates, struct task_info, i);
+
+               if (tsk.oom_score_adj < OOMADJ_BACKGRD_UNLOCKED) {
+                       unsigned int available;
+                       if ((flags & OOM_FORCE) || !(flags & OOM_TIMER_CHECK)) {
+                               _D("%d is skipped during force kill", tsk.pid);
+                               break;
+                       }
+                       available = get_available();
+                       if ((flags & OOM_TIMER_CHECK) &&
+                           (available > thresholds[MEMNOTIFY_MEDIUM] +
+                               THRESHOLD_MARGIN)) {
+                               _D("available: %d MB, larger than threshold margin",
+                                       available);
+                               break;
+                       }
+               }
+
+               selected[num_victims].pid = tsk.pid;
+               selected[num_victims].pgid = tsk.pgid;
+               selected[num_victims].oom_score_adj = tsk.oom_score_adj;
+               selected[num_victims].size = tsk.size;
+               total_victim_size += tsk.size >> 10;
+               num_victims++;
+       }
+
+       g_array_free(victim_candidates, true);
+
+       fclose(f);
+       return num_victims;
+
+}
+
+static inline int is_dynamic_process_killer(int flags) {
+       return ((flags & OOM_FORCE) && !(flags & OOM_NOMEMORY_CHECK));
+}
+
+static int lowmem_swap_cgroup_oom_killer(int flags)
+{
+       int ret;
+       char appname[PATH_MAX];
+       int count = 0;
+       char buf[LOWMEM_PATH_MAX] = {0, };
+       FILE *f;
+       unsigned int tsize = 0;
+       int swap_type;
+
+       swap_type = swap_status(SWAP_GET_TYPE, NULL);
+       if (swap_type <= SWAP_OFF)
+               return count;
+
+       snprintf(buf, sizeof(buf), "%s/memory/swap/cgroup.procs",
+                       MEMCG_PATH);
+
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed, %d", buf, f);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       while (fgets(buf, 32, f) != NULL) {
+               pid_t tpid = 0;
+               int toom = 0;
+
+               tpid = atoi(buf);
+
+               if (proc_get_oom_score_adj(tpid, &toom) < 0) {
+                       _D("pid(%d) was already terminated", tpid);
+                       continue;
+               }
+
+               if (!get_mem_usage_by_pid(tpid, &tsize)) {
+                       _D("pid(%d) size is not available\n", tpid);
+                       continue;
+               }
+
+               /* To Do: skip by checking pgid? */
+               if (toom < OOMADJ_BACKGRD_UNLOCKED)
+                       continue;
+
+               ret = proc_get_cmdline(tpid, appname);
+               if (ret == RESOURCED_ERROR_FAIL)
+                       continue;
+
+               /* make memps log for killing application firstly */
+               if (count == 0)
+                       make_memps_log(MEMPS_LOG_FILE, tpid, appname);
+
+               count++;
+
+               proc_remove_process_list(tpid);
+               kill(tpid, SIGKILL);
+               _E("we killed, lowmem lv2 = %d (%s) score = %d, size = %u KB\n",
+                               tpid, appname, toom, tsize);
+               if (is_dynamic_process_killer(flags) &&
+                   count >= MAX_SWAP_VICTIMS)
+                       break;
+       }
+
+       fclose(f);
+
+       _I("number of swap victims = %d\n", count);
+       if (!(flags & OOM_FORCE) && (count >= MAX_SWAP_VICTIMS))
+               usleep(OOM_MULTIKILL_WAIT);
+
+       return count;
+}
+
+/* Find victims: (SWAP -> ) BACKGROUND */
+static int lowmem_get_memory_cgroup_victims(struct task_info *selected,
+       int flags)
+{
+       int i, swap_victims, count = 0;
+       unsigned int available, should_be_freed = 0;
+
+       swap_victims = lowmem_swap_cgroup_oom_killer(flags);
+
+       if ((flags & OOM_FORCE) && swap_victims < MAX_FD_VICTIMS) {
+               count = lowmem_get_cgroup_victims(MEMCG_BACKGROUND,
+                               MAX_FD_VICTIMS - swap_victims, selected,
+                               0, flags);
+               _D("kill %d victims in %s cgroup",
+                       count, memcg_class[MEMCG_BACKGROUND].cgroup_name);
+               return count;
+       }
+
+       available = get_available();
+       if (available < memcg_class[MEMCG_MEMORY].thres_leave)
+               should_be_freed = memcg_class[MEMCG_MEMORY].thres_leave - available;
+
+       _I("should_be_freed = %u MB", should_be_freed);
+       if (should_be_freed == 0 || swap_victims >= num_max_victims)
+               return count;
+
+       for (i = MEMCG_MAX_GROUPS - 1; i >= 0; i--) {
+               if ((flags & OOM_TIMER_CHECK) && i < MEMCG_BACKGROUND &&
+                       available > thresholds[MEMNOTIFY_MEDIUM]) {
+                       _D("in timer, not kill fg app, available %u > threshold %u",
+                               available, thresholds[MEMNOTIFY_MEDIUM]);
+                       return count;
+               }
+
+               count = lowmem_get_cgroup_victims(i,
+                               num_max_victims - swap_victims,
+                               selected, should_be_freed, flags);
+               if (count > 0) {
+                       _D("kill %d victims in %s cgroup",
+                               count, memcg_class[i].cgroup_name);
+                       return count;
+               } else
+                       _D("There are no victims to be killed in %s cgroup",
+                                       memcg_class[i].cgroup_name);
+
+       }
+
+       return count;
+}
+
+static int lowmem_get_victims(int idx, struct task_info *selected,
+       int flags)
+{
+       int count = 0;
+
+       if (idx == MEMCG_MEMORY)
+               count = lowmem_get_memory_cgroup_victims(selected, flags);
+       else
+               count = lowmem_get_cgroup_victims(idx,
+                                       MAX_CGROUP_VICTIMS, selected,
+                                       memcg_class[idx].thres_leave,
+                                       flags);
+
+       return count;
+}
+
+static Eina_Bool send_sigkill_cb(void *data)
+{
+       int i = 0;
+
+       _D("kill by SIGKILL timer tasks num = %d", killed_tasks.num);
+
+       for (i = 0; i < killed_tasks.num; i++) {
+               kill(killed_tasks.pids[i], SIGKILL);
+               _D("killed %d by SIGKILL", killed_tasks.pids[i]);
+       }
+
+       killed_tasks.num = 0;
+       ecore_timer_del(oom_sigkill_timer);
+       oom_sigkill_timer = NULL;
+       return ECORE_CALLBACK_CANCEL;
+
+}
+
+static int lowmem_kill_victims(int memcg_idx,
+       int count, struct task_info *selected, int flags)
+{
+       const pid_t self = getpid();
+       int pid, ret, oom_score_adj, i;
+       unsigned total_size = 0, size;
+       char appname[PATH_MAX];
+
+       for (i = 0; i < count; i++) {
+               /* check current memory status */
+               if (!(flags & OOM_FORCE) && memcg_idx != MEMCG_MEMORY &&
+                   lowmem_check_current_state(memcg_idx) >= 0)
+                       return count;
+
+               pid = selected[i].pid;
+               oom_score_adj = selected[i].oom_score_adj;
+               size = selected[i].size;
+
+               if (pid <= 0 || self == pid)
+                       continue;
+               ret = proc_get_cmdline(pid, appname);
+               if (ret == RESOURCED_ERROR_FAIL)
+                       continue;
+
+               if (!strcmp("memps", appname)) {
+                       _E("memps(%d) was selected, skip it", pid);
+                       continue;
+               }
+               if (!strcmp("crash-worker", appname)) {
+                       _E("crash-worker(%d) was selected, skip it", pid);
+                       continue;
+               }
+
+               /* make memps log for killing application firstly */
+               if (i==0)
+                       make_memps_log(MEMPS_LOG_FILE, pid, appname);
+
+               total_size += size;
+
+               proc_remove_process_list(pid);
+               if (flags & OOM_FORCE) {
+                       kill(pid, SIGTERM);
+                       if (killed_tasks.num < MAX_MEMORY_CGROUP_VICTIMS)
+                               killed_tasks.pids[killed_tasks.num++] = pid;
+               } else
+                       kill(pid, SIGKILL);
+
+               _E("we killed, force(%d), lowmem lv2 = %d (%s) score = %d, size = %u KB, victim total size = %u KB\n",
+                               flags & OOM_FORCE, pid, appname, oom_score_adj, size, total_size);
+
+               if (memcg_idx >= MEMCG_FOREGROUND &&
+                       memcg_idx < MEMCG_BACKGROUND)
+                       killed_fg_victim = selected[0].pid;
+
+               if (oom_score_adj > OOMADJ_FOREGRD_UNLOCKED)
+                       continue;
+
+               if (i != 0)
+                       make_memps_log(MEMPS_LOG_FILE, pid, appname);
+       }
+
+       return count;
+}
+
+int lowmem_oom_killer_cb(int memcg_idx, int flags)
+{
+       struct task_info selected[MAX_MEMORY_CGROUP_VICTIMS] = {{0, 0, OOMADJ_SU, 0}, };
+       int count = 0;
+
+       /* get multiple victims from /sys/fs/cgroup/memory/.../tasks */
+       count = lowmem_get_victims(memcg_idx, selected, flags);
+
+       if (count == 0) {
+               _D("get %s cgroup victim is failed",
+               memcg_class[memcg_idx].cgroup_name);
+               return count;
+       }
+
+       count = lowmem_kill_victims(memcg_idx, count, selected, flags);
+       clear_logs(MEMPS_LOG_PATH);
+       logging_control(LOGGING_UPDATE_STATE, NULL);
+
+       return count;
+}
+
+void lowmem_dynamic_process_killer(int type)
+{
+       struct task_info selected[MAX_MEMORY_CGROUP_VICTIMS] = {{0, 0, OOMADJ_SU, 0}, };
+       int count = 0;
+       unsigned available = get_available();
+       unsigned should_be_freed;
+       int flags = OOM_FORCE;
+       int swap_victims;
+
+       if (!dynamic_process_threshold[type])
+               return;
+
+       if (available >= dynamic_process_threshold[type])
+               return;
+
+       change_memory_state(MEMNOTIFY_LOW, 1);
+       swap_victims = lowmem_swap_cgroup_oom_killer(flags);
+       if (swap_victims >= MAX_SWAP_VICTIMS)
+               goto start_timer;
+
+       available = get_available();
+       if (available >= dynamic_process_leave[type])
+               return;
+
+       should_be_freed = dynamic_process_leave[type] - available;
+       _D("run dynamic killer, type=%d, available=%d, should_be_freed = %u", type, available, should_be_freed);
+       count = lowmem_get_cgroup_victims(MEMCG_BACKGROUND,
+                       num_max_victims - swap_victims, selected,
+                       should_be_freed, flags);
+
+       if (count == 0) {
+               _D("get victim for dynamic process is failed");
+               return;
+       }
+
+       lowmem_kill_victims(MEMCG_BACKGROUND, count, selected, flags);
+       change_memory_state(MEMNOTIFY_NORMAL, 0);
+
+start_timer:
+       if (oom_sigkill_timer == NULL) {
+               _D("start timer to sigkill tasks");
+               oom_sigkill_timer =
+                       ecore_timer_add(OOM_SIGKILL_WAIT, send_sigkill_cb,
+                                       NULL);
+       } else
+               killed_tasks.num = 0;
+}
+
+static void *lowmem_oom_killer_pthread(void *arg)
+{
+       int ret = 0;
+
+       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, OOM_NONE);
+               _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 char *convert_to_str(int mem_state)
+{
+       char *tmp = NULL;
+       switch (mem_state) {
+       case MEMNOTIFY_NORMAL:
+               tmp = "mem normal";
+               break;
+       case MEMNOTIFY_SWAP:
+               tmp = "mem swap";
+               break;
+       case MEMNOTIFY_LOW:
+               tmp = "mem low";
+               break;
+       case MEMNOTIFY_MEDIUM:
+               tmp = "mem medium";
+               break;
+       default:
+               assert(0);
+       }
+       return tmp;
+}
+
+static void change_lowmem_state(unsigned int mem_state)
+{
+       if (cur_mem_state == mem_state)
+               return;
+
+       _I("[LOW MEM STATE] %s ==> %s", convert_to_str(cur_mem_state),
+               convert_to_str(mem_state));
+       cur_mem_state = mem_state;
+}
+
+static void lowmem_swap_memory(void)
+{
+       pid_t pid;
+       int swap_type;
+
+       if (cur_mem_state == MEMNOTIFY_NORMAL)
+               return;
+
+       swap_type = swap_status(SWAP_GET_TYPE, NULL);
+
+       if (swap_type == SWAP_ON) {
+               while (1)
+               {
+                       pid = (pid_t)swap_status(SWAP_GET_CANDIDATE_PID, NULL);
+                       if (!pid)
+                               break;
+                       _I("swap cgroup entered : pid : %d", (int)pid);
+                       resourced_notify(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, (void*)&pid);
+               }
+               if (swap_status(SWAP_GET_STATUS, NULL) == SWAP_OFF)
+                       resourced_notify(RESOURCED_NOTIFIER_SWAP_RESTART, NULL);
+               resourced_notify(RESOURCED_NOTIFIER_SWAP_START, NULL);
+       }
+}
+
+
+static 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);
+
+       change_lowmem_state(MEMNOTIFY_NORMAL);
+}
+
+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);
+       change_lowmem_state(MEMNOTIFY_SWAP);
+}
+
+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);
+
+       change_lowmem_state(MEMNOTIFY_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);
+}
+
+static Eina_Bool medium_cb(void *data)
+{
+       unsigned int available;
+       int count = 0;
+
+       available = get_available();
+       _D("available = %u, timer run until reaching leave threshold", available);
+
+       if (available >= memcg_class[MEMCG_MEMORY].thres_leave && oom_check_timer != NULL) {
+               ecore_timer_del(oom_check_timer);
+               oom_check_timer = NULL;
+               _D("oom_check_timer deleted after reaching leave threshold");
+               normal_act();
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       _I("cannot reach leave threshold, timer again");
+       count = lowmem_oom_killer_cb(MEMCG_MEMORY, OOM_TIMER_CHECK);
+
+       /*
+        * After running oom killer in timer, but there is no victim,
+        * stop timer.
+        */
+       if (!count && available >= thresholds[MEMNOTIFY_MEDIUM] &&
+           oom_check_timer != NULL) {
+               ecore_timer_del(oom_check_timer);
+               oom_check_timer = NULL;
+               _D("oom_check_timer deleted, available %u > threshold %u",
+                       available, thresholds[MEMNOTIFY_MEDIUM]);
+               normal_act();
+               return ECORE_CALLBACK_CANCEL;
+       }
+       return ECORE_CALLBACK_RENEW;
+}
+
+static void medium_act(void)
+{
+       int ret = 0;
+
+       change_lowmem_state(MEMNOTIFY_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);
+
+       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);
+       }
+
+       return;
+}
+
+static int lowmem_process(int mem_state)
+{
+       int i;
+       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 = %d, new_mem_state = %d\n",
+                               cur_mem_state, mem_state);
+                       lpe[i].action();
+                       return RESOURCED_ERROR_NONE;
+               }
+
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static bool is_fg_victim_killed(int memcg_idx)
+{
+       if (killed_fg_victim) {
+               char buf[LOWMEM_PATH_MAX] = {0, };
+               FILE *f;
+               snprintf(buf, sizeof(buf), "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH,
+                       memcg_idx);
+               f = fopen(buf, "r");
+               if (!f) {
+                       _E("%s open failed, %d", buf, f);
+                       /* if file open fails, start to kill */
+                       return true;
+               }
+
+               while (fgets(buf, 32, f) != NULL) {
+                       pid_t pid = atoi(buf);
+
+                       /*
+                        * not yet removed from foreground cgroup,
+                        * so, not start to kill again
+                        */
+                       if (killed_fg_victim == pid) {
+                               fclose(f);
+                               return false;
+                       }
+               }
+
+               /*
+                * in this case, memory is low even though the previous
+                * fg victim was already killed. so, start to kill.
+                */
+               fclose(f);
+               killed_fg_victim = 0;
+               return true;
+       }
+
+       return true;
+}
+
+static void show_foreground_procs(int memcg_idx) {
+       char buf[LOWMEM_PATH_MAX] = {0, };
+       FILE *f;
+       snprintf(buf, sizeof(buf), "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH,
+               memcg_idx);
+       f = fopen(buf, "r");
+       if (!f) {
+               _E("%s open failed, %d", buf, f);
+               /* if file open fails, start to kill */
+               return;
+       }
+
+       while (fgets(buf, 32, f) != NULL) {
+               pid_t pid = atoi(buf);
+               unsigned int size;
+               get_mem_usage_by_pid(pid, &size);
+               _E("pid = %d, size = %u KB", pid, size);
+       }
+
+       fclose(f);
+}
+
+static void memory_cgroup_medium_act(int memcg_idx)
+{
+       _I("[LOW MEM STATE] memory cgroup %s oom state",
+       memcg_class[memcg_idx].cgroup_name);
+
+       /* only start to kill fg victim when no pending fg victim */
+       if ((memcg_idx >= MEMCG_FOREGROUND && memcg_idx < MEMCG_BACKGROUND)
+           && is_fg_victim_killed(memcg_idx)) {
+               show_foreground_procs(memcg_idx);
+               lowmem_oom_killer_cb(memcg_idx, OOM_NONE);
+       }
+}
+
+static unsigned int lowmem_eventfd_read(int fd)
+{
+       unsigned int ret;
+       uint64_t dummy_state;
+       ret = read(fd, &dummy_state, sizeof(dummy_state));
+       return ret;
+}
+
+static unsigned int check_mem_state(unsigned int available)
+{
+       int mem_state;
+       for (mem_state = MEMNOTIFY_MAX_LEVELS -1; mem_state > MEMNOTIFY_NORMAL; mem_state--) {
+               if (available <= thresholds[mem_state])
+                       break;
+       }
+
+       return mem_state;
+}
+
+void change_memory_state(int state, int force)
+{
+       unsigned int available;
+       int mem_state;
+
+       if (force) {
+               mem_state = state;
+       } else {
+               available = get_available();
+               mem_state = check_mem_state(available);
+               _D("available = %u, mem_state = %d", available, mem_state);
+       }
+
+       switch (mem_state) {
+       case MEMNOTIFY_NORMAL:
+               normal_act();
+               break;
+       case MEMNOTIFY_SWAP:
+               swap_act();
+               break;
+       case MEMNOTIFY_LOW:
+               low_act();
+               break;
+       case MEMNOTIFY_MEDIUM:
+               medium_act();
+               break;
+       default:
+               assert(0);
+       }
+}
+
+static void lowmem_handler(void)
+{
+       static unsigned int prev_available;
+       unsigned int available;
+       int mem_state;
+
+       available = get_available();
+
+       if (prev_available == available)
+               return;
+
+       mem_state = check_mem_state(available);
+       lowmem_process(mem_state);
+       prev_available = available;
+}
+
+static void lowmem_cgroup_handler(int memcg_idx)
+{
+       unsigned int usage;
+       int ret;
+
+       ret = get_mem_usage_anon(memcg_idx, &usage);
+
+       if (ret) {
+               _D("getting anonymous memory usage fails");
+               return;
+       }
+
+       if (usage >= memcg_class[memcg_idx].thres_medium)
+               memory_cgroup_medium_act(memcg_idx);
+       else
+               _I("anon page (%u) is under medium threshold (%u)",
+                       usage >> 20, memcg_class[memcg_idx].thres_medium >> 20);
+}
+
+static Eina_Bool lowmem_cb(void *data, Ecore_Fd_Handler *fd_handler)
+{
+       int fd, i;
+
+       if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
+               _E("ecore_main_fd_handler_active_get error , return\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+
+       fd = ecore_main_fd_handler_fd_get(fd_handler);
+       if (fd < 0) {
+               _E("ecore_main_fd_handler_fd_get error , return\n");
+               return ECORE_CALLBACK_CANCEL;
+       }
+       lowmem_eventfd_read(fd);
+
+       for (i = 0; i < MEMCG_MAX_GROUPS; i++) {
+               if (fd == evfd[i]) {
+                       if (i == MEMCG_MEMORY) {
+                               lowmem_handler();
+                       } else {
+                               lowmem_cgroup_handler(i);
+                       }
+               }
+       }
+
+       return ECORE_CALLBACK_RENEW;
+}
+
+/*
+From memory.txt kernel document -
+To register a notifier, application need:
+- create an eventfd using eventfd(2)
+- open memory.oom_control file
+- write string like "<event_fd> <fd of memory.oom_control>"
+to cgroup.event_control
+*/
+
+static int setup_eventfd(void)
+{
+       unsigned int i;
+       int cgfd, pressurefd, res, sz;
+       char buf[LOWMEM_PATH_MAX] = {0,};
+
+
+       for (i = 0; i < MEMCG_MAX_GROUPS; i++) {
+               if (memcg_class[i].event_string == NULL)
+                       continue;
+               /* open cgroup.event_control */
+               snprintf(buf, sizeof(buf), "%s/%s/cgroup.event_control",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               cgfd = open(buf, O_WRONLY);
+               if (cgfd < 0) {
+                       _E("open event_control failed");
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* register event pressure_level */
+               snprintf(buf, sizeof(buf), "%s/%s/memory.pressure_level",
+                               MEMCG_PATH, memcg_class[i].cgroup_name);
+               pressurefd = open(buf, O_RDONLY);
+               if (pressurefd < 0) {
+                       _E("open pressure control failed");
+                       close(cgfd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* create an eventfd using eventfd(2)
+                  use same event fd for using ecore event loop */
+               evfd[i] = eventfd(0, O_NONBLOCK);
+               if (evfd[i] < 0) {
+                       _E("eventfd() error");
+                       close(cgfd);
+                       close(pressurefd);
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               /* pressure level*/
+               /* write event fd low level */
+               sz = snprintf(buf, sizeof(buf), "%d %d %s", evfd[i], pressurefd,
+                               memcg_class[i].event_string);
+               sz += 1;
+               res = write(cgfd, buf, sz);
+               if (res != sz) {
+                       _E("write cgfd failed : %d for %s",
+                               res, memcg_class[i].cgroup_name);
+                       close(cgfd);
+                       close(pressurefd);
+                       close(evfd[i]);
+                       evfd[i] = -1;
+                       return RESOURCED_ERROR_FAIL;
+               }
+
+               _I("register event fd success for %s cgroup",
+                       memcg_class[i].cgroup_name);
+               ecore_main_fd_handler_add(evfd[i], ECORE_FD_READ,
+                               (Ecore_Fd_Cb)lowmem_cb, NULL, NULL, NULL);
+
+               close(cgfd);
+               close(pressurefd);
+       }
+       return 0;
+}
+
+static int write_cgroup_node(const char *memcg_name,
+               const char *file_name, unsigned int value)
+{
+       FILE *f = NULL;
+       char buf[LOWMEM_PATH_MAX] = {0, };
+       int size;
+
+       snprintf(buf, sizeof(buf), "%s/%s/%s", MEMCG_PATH, memcg_name, file_name);
+       f = fopen(buf, "w");
+       if (!f) {
+               _E("%s open failed", buf);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       size = snprintf(buf, sizeof(buf), "%u", value);
+       if (fwrite(buf, size, 1, f) != 1) {
+               _E("fail fwrite %s\n", file_name);
+               fclose(f);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       fclose(f);
+       return RESOURCED_ERROR_NONE;
+}
+
+void set_threshold(int level, int thres)
+{
+       thresholds[level] = thres;
+       return;
+}
+
+void set_leave_threshold(int thres)
+{
+       memcg_class[MEMCG_MEMORY].thres_leave = thres;
+       return;
+}
+
+void set_foreground_ratio(float ratio)
+{
+       int i;
+       for (i = MEMCG_FOREGROUND; i < MEMCG_BACKGROUND; i++)
+               memcg_class[i].limit_ratio = ratio;
+       return;
+}
+
+static int load_mem_config(struct parse_result *result, void *user_data)
+{
+       pid_t pid = 0;
+       if (!result)
+               return -EINVAL;
+
+       if (strcmp(result->section, MEM_CONF_SECTION))
+               return RESOURCED_ERROR_NONE;
+
+       if (!strcmp(result->name, MEM_CONF_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 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);
+               set_threshold(MEMNOTIFY_SWAP, value);
+       } else if (!strcmp(result->name, "ThresholdLow")) {
+               int value = atoi(result->value);
+               set_threshold(MEMNOTIFY_LOW, value);
+       } else if (!strcmp(result->name, "ThresholdMedium")) {
+               int value = atoi(result->value);
+               set_threshold(MEMNOTIFY_MEDIUM, value);
+       } else if (!strcmp(result->name, "ThresholdLeave")) {
+               int value = atoi(result->value);
+               set_leave_threshold(value);
+       } else if (!strcmp(result->name, "ForegroundRatio")) {
+               float value = atof(result->value);
+               set_foreground_ratio(value);
+       } else if (!strcmp(result->name, "NumMaxVictims")) {
+               int value = atoi(result->value);
+               num_max_victims = value;
+               _D("set number of max victims as %d", num_max_victims);
+       } else if (!strcmp(result->name, "DynamicThreshold")) {
+               int value = atoi(result->value);
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = value;
+               _D("set dynamic process threshold as %d",
+                       dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP]);
+       } else if (!strcmp(result->name, "DynamicLeave")) {
+               int value = atoi(result->value);
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = value;
+               _D("set dynamic process leave threshold as %d",
+                       dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP]);
+       } else if (!strcmp(result->name, "DynamicThresholdLaunch")) {
+               int value = atoi(result->value);
+               dynamic_process_threshold[DYNAMIC_KILL_LUNCH] = value;
+               _D("set dynamic process threshold as %d",
+                       dynamic_process_threshold[DYNAMIC_KILL_LUNCH]);
+       } else if (!strcmp(result->name, "DynamicLeaveLaunch")) {
+               int value = atoi(result->value);
+               dynamic_process_leave[DYNAMIC_KILL_LUNCH] = value;
+               _D("set dynamic process leave threshold as %d",
+                       dynamic_process_leave[DYNAMIC_KILL_LUNCH]);
+       }
+       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_512_config(struct parse_result *result, void *user_data)
+{
+       return set_memory_config("Memory512", 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);
+}
+
+/* init thresholds depending on total ram size. */
+static void init_thresholds(void)
+{
+       int i;
+       unsigned long total_ramsize = BtoMB(totalram);
+       _D("Total : %lu MB", total_ramsize);
+
+       if (total_ramsize <= MEM_SIZE_64) {
+               /* set thresholds for ram size 64M */
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_64_THRES;
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_64_LEAVE;
+               set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_64_THRES_SWAP);
+               set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_64_THRES_LOW);
+               set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_64_THRES_MEDIUM);
+               set_leave_threshold(MEMCG_MEMORY_64_THRES_LEAVE);
+               config_parse(MEM_CONF_FILE, memory_load_64_config, NULL);
+       } else if (total_ramsize <= MEM_SIZE_256) {
+               /* set thresholds for ram size 256M */
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_256_THRES;
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_256_LEAVE;
+               set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_256_THRES_SWAP);
+               set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_256_THRES_LOW);
+               set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_256_THRES_MEDIUM);
+               set_leave_threshold(MEMCG_MEMORY_256_THRES_LEAVE);
+               config_parse(MEM_CONF_FILE, memory_load_256_config, NULL);
+       } else if (total_ramsize <= MEM_SIZE_512) {
+               /* set thresholds for ram size 512M */
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_512_THRES;
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_512_LEAVE;
+               dynamic_process_threshold[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_512_THRESLAUNCH;
+               dynamic_process_leave[DYNAMIC_KILL_LUNCH] = DYNAMIC_PROCESS_512_LEAVELAUNCH;
+               set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_512_THRES_SWAP);
+               set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_512_THRES_LOW);
+               set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_512_THRES_MEDIUM);
+               set_leave_threshold(MEMCG_MEMORY_512_THRES_LEAVE);
+               config_parse(MEM_CONF_FILE, memory_load_512_config, NULL);
+       } else if (total_ramsize <= MEM_SIZE_1024) {
+               /* set thresholds for ram size more than 1G */
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_1024_THRES;
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_1024_LEAVE;
+               set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_1024_THRES_SWAP);
+               set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_1024_THRES_LOW);
+               set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_1024_THRES_MEDIUM);
+               set_leave_threshold(MEMCG_MEMORY_1024_THRES_LEAVE);
+               config_parse(MEM_CONF_FILE, memory_load_1024_config, NULL);
+       } else {
+               dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_2048_THRES;
+               dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP] = DYNAMIC_PROCESS_2048_LEAVE;
+               set_threshold(MEMNOTIFY_SWAP, MEMCG_MEMORY_2048_THRES_SWAP);
+               set_threshold(MEMNOTIFY_LOW, MEMCG_MEMORY_2048_THRES_LOW);
+               set_threshold(MEMNOTIFY_MEDIUM, MEMCG_MEMORY_2048_THRES_MEDIUM);
+               set_leave_threshold(MEMCG_MEMORY_2048_THRES_LEAVE);
+               config_parse(MEM_CONF_FILE, memory_load_2048_config, NULL);
+       }
+
+       for (i = MEMNOTIFY_SWAP; i < MEMNOTIFY_MAX_LEVELS; i++)
+               _I("set threshold for %d to %u", i, thresholds[i]);
+
+       _I("set thres_leave to %u", memcg_class[MEMCG_MEMORY].thres_leave);
+       _I("set dynamic process threshold to %u", dynamic_process_threshold[DYNAMIC_KILL_LARGEHEAP]);
+       _I("set dynamic process leave to %u", dynamic_process_leave[DYNAMIC_KILL_LARGEHEAP]);
+}
+
+static int create_foreground_memcg(void)
+{
+       int i;
+       char buf[LOWMEM_PATH_MAX] = {0, };
+       for (i = MEMCG_FOREGROUND; i < MEMCG_MAX_GROUPS; i++) {
+               snprintf(buf, sizeof(buf), "%s/%s", MEMCG_PATH, memcg_class[i].cgroup_name);
+               if (mkdir(buf, 0755) && errno != EEXIST) {
+                       _E("mkdir %s failed, errno %d", buf, errno);
+                       return RESOURCED_ERROR_FAIL;
+               }
+               _I("%s is successfuly created", buf);
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static int init_memcg(void)
+{
+       unsigned int i, limit;
+       _D("Total : %lu", totalram);
+       int ret = RESOURCED_ERROR_NONE;
+
+       for (i = 0; i < MEMCG_MAX_GROUPS; i++) {
+               /* enable cgroup move */
+               ret = write_cgroup_node(memcg_class[i].cgroup_name,
+                                       MEMCG_MOVE_CHARGE_PATH, 3);
+               if (ret)
+                       return ret;
+
+                /* for memcg with NO_LIMIT, do not set limit for cgroup limit */
+               if (memcg_class[i].limit_ratio == NO_LIMIT)
+                       continue;
+
+               /* disable memcg OOM-killer */
+               ret = write_cgroup_node(memcg_class[i].cgroup_name,
+                                       MEMCG_OOM_CONTROL_PATH, 1);
+               if (ret)
+                       return ret;
+
+               /* write limit_in_bytes */
+               limit = (unsigned int)(memcg_class[i].limit_ratio*(float)totalram);
+               if (limit > memcg_class[i].min_limit)
+                       limit = memcg_class[i].min_limit;
+               ret = write_cgroup_node(memcg_class[i].cgroup_name,
+                                       MEMCG_LIMIT_PATH, limit);
+               if (ret)
+                       return ret;
+               else
+                       _I("set %s's limit to %u", memcg_class[i].cgroup_name, limit);
+
+               if (BtoMB(totalram) < MEM_SIZE_512 &&
+                       (i >= MEMCG_FOREGROUND && i < MEMCG_BACKGROUND)) {
+                       memcg_class[i].thres_leave = limit * MEMCG_FOREGROUND_LEAVE_RATIO;
+                       _I("set foreground%d leave %u for limit %u",
+                               i, memcg_class[i].thres_leave, limit);
+               }
+
+               /* set threshold and oomleave for each memcg */
+               memcg_class[i].thres_low =
+                       (unsigned int)(limit * MEMCG_LOW_RATIO);
+               memcg_class[i].thres_medium =
+                       (unsigned int)(limit * MEMCG_MEDIUM_RATIO);
+               memcg_class[i].oomleave =
+                       limit - (memcg_class[i].thres_leave << 20);
+       }
+
+       return ret;
+}
+
+static void lowmem_check(void)
+{
+       unsigned int available;
+
+       available = get_available();
+       _D("available = %u", available);
+
+       if(cur_mem_state != MEMNOTIFY_SWAP &&
+               (available <= thresholds[MEMNOTIFY_SWAP] &&
+                       available > thresholds[MEMNOTIFY_LOW])) {
+               swap_act();
+
+       }
+}
+
+static int find_foreground_cgroup(struct proc_process_info_t *process_info) {
+       int fg, min_fg = -1;
+       unsigned int min_usage = UINT_MAX;
+
+       /*
+        * if this process group is already in one of the foreground cgroup,
+        * put all of the process in this group into the same cgroup.
+        */
+       if (process_info && process_info->memcg_idx >= MEMCG_FOREGROUND &&
+           process_info->memcg_idx < MEMCG_FOREGROUND + NUM_FOREGROUND)
+               return process_info->memcg_idx;
+
+       /*
+        * if any of the process in this group is not in foreground,
+        * find foreground cgroup with minimum usage
+        */
+       for (fg = MEMCG_FOREGROUND; fg < MEMCG_BACKGROUND; fg++) {
+               unsigned int usage;
+               usage = get_mem_usage(fg);
+
+               /* select foreground memcg with no task first*/
+               if (usage == 0)
+                       return fg;
+
+               /* select forground memcg with minimum usage */
+               if (usage > 0 && min_usage > usage) {
+                       min_usage = usage;
+                       min_fg = fg;
+               }
+       }
+
+       if (min_fg < 0)
+               return RESOURCED_ERROR_FAIL;
+
+       return min_fg;
+}
+
+static void lowmem_move_memcgroup(int pid, int oom_score_adj)
+{
+       char buf[LOWMEM_PATH_MAX] = {0,};
+       FILE *f;
+       int size, background = 0;
+       unsigned long swap_args[1] = {0,};
+       struct proc_process_info_t *process_info =
+               find_process_info(NULL, pid, NULL);
+
+       if (oom_score_adj >= OOMADJ_BACKGRD_LOCKED) {
+               snprintf(buf, sizeof(buf), "%s/memory/background/cgroup.procs", MEMCG_PATH);
+               proc_set_process_info_memcg(process_info, MEMCG_BACKGROUND);
+               background = 1;
+       } else if (oom_score_adj >= OOMADJ_FOREGRD_LOCKED &&
+                                       oom_score_adj < OOMADJ_BACKGRD_LOCKED) {
+               int ret = find_foreground_cgroup(process_info);
+               if (ret == RESOURCED_ERROR_FAIL) {
+                       _E("cannot find foreground cgroup");
+                       return;
+               }
+               snprintf(buf, sizeof(buf), "%s/memory/foreground%d/cgroup.procs", MEMCG_PATH, ret);
+               proc_set_process_info_memcg(process_info, ret);
+       } else
+               return;
+
+       swap_args[0] = (unsigned long)pid;
+       if (!swap_status(SWAP_CHECK_PID, swap_args) || !background) {
+               _D("buf : %s, pid : %d, score : %d", buf, pid, oom_score_adj);
+               f = fopen(buf, "w");
+               if (!f) {
+                       _E("%s open failed", buf);
+                       return;
+               }
+               size = snprintf(buf, sizeof(buf), "%d", pid);
+               if (fwrite(buf, size, 1, f) != 1)
+                       _E("fwrite cgroup tasks : %d\n", pid);
+               fclose(f);
+       }
+       if (background) {
+               lowmem_check();
+               lowmem_swap_memory();
+       }
+}
+
+static void lowmem_cgroup_foregrd_manage(int currentpid)
+{
+       GSList *iter;
+       struct proc_process_info_t *process_info =
+               find_process_info(NULL, currentpid, NULL);
+
+       if (!process_info)
+               return;
+
+       gslist_for_each_item(iter, process_info->pids) {
+               struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
+
+               if (pid_info->type == RESOURCED_APP_TYPE_GROUP)
+                       lowmem_move_memcgroup(pid_info->pid, OOMADJ_FOREGRD_UNLOCKED);
+       }
+}
+
+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 lowmem_app_launch_cb(void *data)
+{
+       struct proc_status *p_data = (struct proc_status*)data;
+       struct proc_process_info_t *process_info;
+       int ret = 0;
+       ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL,
+               "Please provide valid argument!");
+       process_info = (struct proc_process_info_t *)p_data->processinfo;
+
+       if (process_info && !(process_info->type & PROC_LARGE_HEAP))
+               lowmem_dynamic_process_killer(DYNAMIC_KILL_LUNCH);
+       return ret;
+}
+
+/* To Do: should we need lowmem_fd_start, lowmem_fd_stop ?? */
+int lowmem_init(void)
+{
+       int ret = RESOURCED_ERROR_NONE;
+
+       ret = create_foreground_memcg();
+
+       if (ret) {
+               _E("create foreground memcgs failed");
+               return ret;
+       }
+       get_total_memory();
+       init_thresholds();
+       config_parse(MEM_CONF_FILE, load_mem_config, NULL);
+
+       ret = oom_thread_create();
+       if (ret) {
+               _E("oom thread create failed\n");
+               return ret;
+       }
+
+       /* set default memcg value */
+       ret = init_memcg();
+       if (ret) {
+               _E("memory cgroup init failed");
+               return ret;
+       }
+
+       /* register threshold and event fd */
+       ret = setup_eventfd();
+       if (ret) {
+               _E("eventfd setup failed");
+               return ret;
+       }
+
+       lowmem_dbus_init();
+       register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_app_launch_cb);
+
+       return ret;
+}
+
+static int resourced_memory_control(void *data)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct lowmem_data_type *l_data;
+
+       l_data = (struct lowmem_data_type *)data;
+       switch(l_data->control_type) {
+       case LOWMEM_MOVE_CGROUP:
+               if (l_data->args)
+                       lowmem_move_memcgroup((pid_t)l_data->args[0], l_data->args[1]);
+               break;
+       case LOWMEM_MANAGE_FOREGROUND:
+               if (l_data->args)
+                       lowmem_cgroup_foregrd_manage((pid_t)l_data->args[0]);
+               break;
+
+       }
+       return ret;
+}
+
+static int resourced_memory_init(void *data)
+{
+       lowmem_ops = &memory_modules_ops;
+
+       return lowmem_init();
+}
+
+static int resourced_memory_finalize(void *data)
+{
+       unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, lowmem_app_launch_cb);
+       return RESOURCED_ERROR_NONE;
+}
+
+int lowmem_control(enum lowmem_control_type type, unsigned long *args)
+{
+       struct lowmem_data_type l_data;
+
+       if (lowmem_ops) {
+               l_data.control_type = type;
+               l_data.args = args;
+               return lowmem_ops->control(&l_data);
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static const struct module_ops memory_modules_ops = {
+       .priority       = MODULE_PRIORITY_NORMAL,
+       .name           = "lowmem",
+       .init           = resourced_memory_init,
+       .exit           = resourced_memory_finalize,
+       .control        = resourced_memory_control,
+};
+
+MODULE_REGISTER(&memory_modules_ops)
diff --git a/src/memps/CMakeLists.txt b/src/memps/CMakeLists.txt
new file mode 100644 (file)
index 0000000..83e6fca
--- /dev/null
@@ -0,0 +1,19 @@
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -fPIC -Wall -Werror")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+
+IF("${ARCH}" STREQUAL "arm")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+SET(SOURCES ${MEMPS_SOURCE_DIR}/memps.c)
+
+ADD_EXECUTABLE(memps ${MEMPS_SOURCE_DIR}/memps.c)
+INSTALL(TARGETS memps DESTINATION /usr/bin)
+
+
diff --git a/src/memps/memps.c b/src/memps/memps.c
new file mode 100644 (file)
index 0000000..4c18d72
--- /dev/null
@@ -0,0 +1,1127 @@
+/*
+   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>
+
+#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 BUF_MAX         (BUFSIZ)            /* most optimal for libc::stdio */
+#define BUF_INC_SIZE    (512 * 1024)        /* maximal SMAPS I saw 2 MB     */
+#define KB(bytes)       ((bytes)/1024)
+
+typedef struct geminfo geminfo;
+typedef struct mapinfo mapinfo;
+typedef struct trib_mapinfo trib_mapinfo;
+
+enum {
+       OUTPUT_UART,
+       OUTPUT_FILE,
+       NUM_OUTPUT_TYPE
+};
+
+struct mapinfo {
+       mapinfo *next;
+       unsigned start;
+       unsigned end;
+       unsigned size;
+       unsigned rss;
+       unsigned pss;
+       unsigned shared_clean;
+       unsigned shared_dirty;
+       unsigned private_clean;
+       unsigned private_dirty;
+       char perm[4];
+       char name[1];
+};
+
+/* 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 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 int ignore_smaps_field;
+static int sum;
+static int verbos;
+
+/* reads file contents into memory */
+static char* cread(const char* path)
+{
+       /* once allocated area for reads */
+       static char*    text = NULL;
+       static size_t   size = 0;
+
+       ssize_t ret;
+       char*   ptr = text;
+       size_t  cap = size;
+       int     fd  = open(path, O_RDONLY);
+
+       if (fd < 0) {
+               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;
+               }
+       } while (ret > 0);
+       close(fd);
+
+       return (ret < 0 ? NULL : text);
+} /* cread */
+
+/* like fgets/gets but adjusting contents pointer */
+static inline 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;
+} /* cgets */
+
+
+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) {
+               fprintf(stderr, "cannot open %s\n", 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) {
+               if (sscanf(line, "%d %d %d %d %d 0x%x",
+                       &pid, &tgid, &handle, &refcount,
+                       &hcount, &gem_size) != NUM_GEM_FIELD)
+                       return NULL;
+
+               tgeminfo = malloc(sizeof(geminfo));
+               if (tgeminfo == NULL)
+                       return NULL;
+               tgeminfo->tgid = tgid;
+               tgeminfo->hcount = hcount;
+               tgeminfo->rss_size = KB(gem_size);
+               tgeminfo->pss_size = KB(gem_size/tgeminfo->hcount);
+       } else
+               return NULL;
+
+       return tgeminfo;
+}
+
+
+static geminfo *load_geminfo(void)
+{
+       geminfo *ginfo;
+       geminfo *gilist = NULL;
+       FILE *drm_fp;
+       char line[BUF_MAX];
+
+       drm_fp = fopen("/sys/kernel/debug/dri/0/gem_info", "r");
+
+       if (drm_fp == NULL) {
+               fprintf(stderr,
+               "cannot open /sys/kernel/debug/dri/0/gem_info\n");
+               return NULL;
+       }
+
+       if (fgets(line, BUF_MAX, drm_fp) == NULL) {
+               fclose(drm_fp);
+               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) {
+                       fclose(drm_fp);
+                       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;
+       }
+
+       fclose(drm_fp);
+
+       return gilist;
+}
+
+
+/* 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /android/lib/libcomposer.so
+ * 012345678901234567890123456789012345678901234567890123456789
+ * 0         1         2         3         4         5
+ */
+
+mapinfo *read_mapinfo(char** smaps, int rest_line)
+{
+       char* line;
+       mapinfo *mi;
+       int len;
+       int tmp;
+
+       if ((line = cgets(smaps)) == 0)
+               return 0;
+
+       len    = strlen(line);
+       if (len < 1)
+               return 0;
+
+       mi = malloc(sizeof(mapinfo) + len + 16);
+       if (mi == 0)
+               return 0;
+
+       mi->start = strtoul(line, 0, 16);
+       mi->end = strtoul(line + 9, 0, 16);
+
+       mi->perm[0] = line[18]; /* read */
+       mi->perm[1] = line[19]; /* write */
+       mi->perm[2] = line[20]; /* execute */
+       mi->perm[3] = line[21]; /* may share or private */
+
+       if (len < 50)
+               snprintf(mi->name, len+17, "%s", "[anon]");
+       else
+               snprintf(mi->name, len+17, "%s", line + 49);
+
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Size: %d kB", &mi->size) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Rss: %d kB", &mi->rss) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Pss: %d kB", &mi->pss) == 1)
+               if ((line = cgets(smaps)) == 0)
+                       goto oops;
+       if (sscanf(line, "Shared_Clean: %d kB", &mi->shared_clean) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Shared_Dirty: %d kB", &mi->shared_dirty) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Private_Clean: %d kB", &mi->private_clean) != 1)
+               goto oops;
+       if ((line = cgets(smaps)) == 0)
+               goto oops;
+       if (sscanf(line, "Private_Dirty: %d kB", &mi->private_dirty) != 1)
+               goto oops;
+
+       while (rest_line-- && (line = cgets(smaps))) {
+               if (sscanf(line, "PSwap: %d kB", &tmp) == 1)
+                       rest_line++;
+       }
+
+       return mi;
+ oops:
+       printf("mi get error\n");
+       free(mi);
+       return 0;
+}
+
+static unsigned total_gem_memory(void)
+{
+       FILE *gem_fp;
+       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) {
+               fprintf(stderr,
+               "cannot open /proc/dir/0/gem_names\n");
+               return 0;
+       }
+
+       if (fgets(line, BUF_MAX, gem_fp) == NULL) {
+               fclose(gem_fp);
+               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;
+       fclose(gem_fp);
+
+       return total_gem_mem;
+}
+
+static void get_mem_info(FILE *output_fp)
+{
+       char buf[PATH_MAX];
+       FILE *fp;
+       char *idx;
+       unsigned int free = 0, cached = 0;
+       unsigned int total_mem = 0, available = 0, used;
+       unsigned int swap_total = 0, swap_free = 0, swap_used;
+       unsigned int used_ratio;
+
+       if (output_fp == NULL)
+               return;
+
+       fp = fopen("/proc/meminfo", "r");
+
+       if (!fp) {
+               fprintf(stderr, "%s open failed, %p", buf, fp);
+               return;
+       }
+
+       while (fgets(buf, PATH_MAX, fp) != NULL) {
+               if ((idx = strstr(buf, "MemTotal:"))) {
+                       idx += strlen("Memtotal:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       total_mem = atoi(idx);
+               } else if ((idx = strstr(buf, "MemFree:"))) {
+                       idx += strlen("MemFree:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       free = atoi(idx);
+               } else if ((idx = strstr(buf, "MemAvailable:"))) {
+                       idx += strlen("MemAvailable:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       available = atoi(idx);
+               } else if((idx = strstr(buf, "Cached:")) && !strstr(buf, "Swap")) {
+                       idx += strlen("Cached:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       cached = atoi(idx);
+               } else if((idx = strstr(buf, "SwapTotal:"))) {
+                       idx += strlen("SwapTotal:");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       swap_total = atoi(idx);
+               } else if((idx = strstr(buf, "SwapFree:"))) {
+                       idx += strlen("SwapFree");
+                       while (*idx < '0' || *idx > '9')
+                               idx++;
+                       swap_free = atoi(idx);
+                       break;
+               }
+       }
+
+       if (available == 0)
+               available = free + cached;
+       used = total_mem - available;
+       used_ratio = used * 100 / total_mem;
+       swap_used = swap_total - swap_free;
+
+       fprintf(output_fp,
+               "====================================================================\n");
+
+
+       fprintf(output_fp, "Total RAM size: \t%15d MB( %6d kB)\n",
+                       total_mem >> 10, total_mem);
+
+       fprintf(output_fp, "Used (Mem+Reclaimable): %15d MB( %6d kB)\n",
+                       (total_mem - free) >> 10, total_mem - free);
+
+       fprintf(output_fp, "Used (Mem+Swap): \t%15d MB( %6d kB)\n",
+                       used >> 10, used);
+
+       fprintf(output_fp, "Used (Mem):  \t\t%15d MB( %6d kB)\n",
+                       used >> 10, used);
+
+       fprintf(output_fp, "Used (Swap): \t\t%15d MB( %6d kB)\n",
+                       swap_used >> 10, swap_used);
+
+       fprintf(output_fp, "Used Ratio: \t\t%15d  %%\n", used_ratio);
+
+       fprintf(output_fp, "Mem Free:\t\t%15d MB( %6d kB)\n",
+                       free >> 10, free);
+
+       fprintf(output_fp, "Available (Free+Reclaimable):%10d MB( %6d kB)\n",
+                       available >> 10,
+                       available);
+       fclose(fp);
+}
+
+static int get_tmpfs_info(FILE *output_fp)
+{
+       FILE *fp;
+       char line[BUF_MAX];
+       char tmpfs_mp[NAME_MAX];        /* tmpfs mount point */
+       struct statfs tmpfs_info;
+
+       if (output_fp == NULL)
+               return -1;
+
+       fp = fopen("/etc/mtab", "r");
+       if (fp == NULL)
+               return -1;
+
+       fprintf(output_fp,
+               "====================================================================\n");
+       fprintf(output_fp, "TMPFS INFO\n");
+
+       while (fgets(line, BUF_MAX, fp) != NULL) {
+               if (sscanf(line, "tmpfs %s tmpfs", tmpfs_mp) == 1) {
+                       statfs(tmpfs_mp, &tmpfs_info);
+                       fprintf(output_fp,
+                               "tmpfs %16s  Total %8ld KB, Used %8ld, Avail %8ld\n",
+                               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);
+               }
+       }
+       fclose(fp);
+       return 0;
+}
+
+mapinfo *load_maps(int pid)
+{
+       char* smaps;
+       char tmp[128];
+       mapinfo *milist = 0;
+       mapinfo *mi;
+
+       snprintf(tmp, sizeof(tmp), "/proc/%d/smaps", pid);
+       smaps = cread(tmp);
+       if (smaps == NULL)
+               return 0;
+
+       while ((mi = read_mapinfo(&smaps, ignore_smaps_field)) != 0) {
+               if (milist) {
+                       if ((!strcmp(mi->name, milist->name)
+                            && (mi->name[0] != '['))) {
+                               milist->size += mi->size;
+                               milist->rss += mi->rss;
+                               milist->pss += mi->pss;
+                               milist->shared_clean += mi->shared_clean;
+                               milist->shared_dirty += mi->shared_dirty;
+                               milist->private_clean += mi->private_clean;
+                               milist->private_dirty += mi->private_dirty;
+
+                               milist->perm[0] = mi->perm[0];
+                               milist->perm[1] = mi->perm[1];
+                               milist->perm[2] = mi->perm[2];
+                               milist->perm[3] = mi->perm[3];
+                               milist->end = mi->end;
+                               strncpy(milist->perm, mi->perm, 4);
+                               free(mi);
+                               continue;
+                       }
+               }
+               mi->next = milist;
+               milist = mi;
+       }
+
+       return milist;
+}
+
+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->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, mapinfo *milist,
+                geminfo *gilist, trib_mapinfo *result)
+
+{
+       mapinfo *mi;
+       mapinfo *temp = NULL;
+       geminfo *gi;
+
+       if (!result)
+               return -EINVAL;
+
+       init_trib_mapinfo(result);
+       for (mi = milist; mi;) {
+               if (strstr(mi->name, STR_SGX_PATH)) {
+                       result->graphic_3d += mi->pss;
+               } else if (strstr(mi->name, STR_3D_PATH1) ||
+                       strstr(mi->name, STR_3D_PATH2)) {
+                       result->graphic_3d += mi->size;
+               } else if (mi->rss != 0 && mi->pss == 0
+                          && mi->shared_clean == 0
+                          && mi->shared_dirty == 0
+                          && mi->private_clean == 0
+                          && mi->private_dirty == 0) {
+                       result->other_devices += mi->size;
+               } else if (!strncmp(mi->name, STR_DRM_PATH1,
+                               sizeof(STR_DRM_PATH1)) ||
+                               !strncmp(mi->name, STR_DRM_PATH2,
+                               sizeof(STR_DRM_PATH2))) {
+                       result->gem_mmap += mi->rss;
+               } else {
+                       result->shared_clean += mi->shared_clean;
+                       result->shared_dirty += mi->shared_dirty;
+                       result->private_clean += mi->private_clean;
+                       result->private_dirty += mi->private_dirty;
+                       result->rss += mi->rss;
+                       result->pss += mi->pss;
+                       result->size += mi->size;
+
+                       if(mi->shared_clean != 0)
+                               result->shared_clean_pss += mi->pss;
+                       else if (mi->shared_dirty != 0)
+                               result->shared_dirty_pss += mi->pss;
+               }
+
+               temp = mi;
+               mi = mi->next;
+               free(temp);
+               temp = NULL;
+       }
+
+       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)
+{
+       FILE *fp;
+       char buf[NAME_MAX] = {0, };
+       int ret = -1;
+
+       snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
+       fp = fopen(buf, "r");
+       if (fp == 0) {
+               fprintf(stderr, "cannot file open %s\n", buf);
+               return ret;
+       }
+       if ((ret = fscanf(fp, "%s", cmdline)) < 1) {
+               fclose(fp);
+               return ret;
+       }
+       fclose(fp);
+
+       return ret;
+}
+
+static int get_oomscoreadj(unsigned int pid)
+{
+       FILE *fp;
+       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;
+               fclose(fp);
+               return oomadj_val;
+       }
+
+       oomadj_val = atoi(tmp);
+
+       fclose(fp);
+       return oomadj_val;
+}
+
+static void get_rss(pid_t pid, unsigned int *result)
+{
+       FILE *fp;
+       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) {
+               fclose(fp);
+               return;
+       }
+
+       fclose(fp);
+
+       /* convert page to Kb */
+       *result = rss * 4;
+       return;
+}
+
+static void show_rss(int output_type, char *output_path)
+{
+       DIR *pDir = NULL;
+       struct dirent curdir;
+       struct dirent *result;
+       pid_t pid;
+       char cmdline[PATH_MAX];
+       FILE *output_file = NULL;
+       int oom_score_adj;
+       unsigned int rss;
+       int ret;
+
+       pDir = opendir("/proc");
+       if (pDir == NULL) {
+               fprintf(stderr, "cannot read directory /proc.\n");
+               return;
+       }
+
+       if (output_type == OUTPUT_FILE && output_path) {
+               output_file = fopen(output_path, "w+");
+               if (!output_file) {
+                       fprintf(stderr, "cannot open output file(%s)\n",
+                               output_path);
+                       closedir(pDir);
+                       exit(1);
+               }
+       } else
+               output_file = stdout;
+
+
+       fprintf(output_file,
+                       "     PID    RSS    OOM_SCORE    COMMAND\n");
+
+       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);
+
+               fprintf(output_file,
+                               "%8d %8u %8d          %s\n",
+                               pid,
+                               rss,
+                               oom_score_adj,
+                               cmdline);
+       } /* end of while */
+
+       get_tmpfs_info(output_file);
+       get_mem_info(output_file);
+
+       fclose(output_file);
+       closedir(pDir);
+
+       return;
+}
+
+static int show_map_all_new(int output_type, char *output_path)
+{
+       DIR *pDir = NULL;
+       struct dirent curdir;
+       struct dirent *result;
+       unsigned int pid;
+       mapinfo *milist;
+       geminfo *glist;
+       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_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];
+       FILE *output_file = NULL;
+       int oom_score_adj;
+       int ret;
+
+       pDir = opendir("/proc");
+       if (pDir == NULL) {
+               fprintf(stderr, "cannot read directory /proc.\n");
+               return 0;
+       }
+
+       if (output_type == OUTPUT_FILE && output_path) {
+               output_file = fopen(output_path, "w+");
+               if (!output_file) {
+                       fprintf(stderr, "cannot open output file(%s)\n",
+                               output_path);
+                       closedir(pDir);
+                       exit(1);
+               }
+       } else
+               output_file = stdout;
+
+       glist = load_geminfo();
+
+       if (!sum) {
+               if (verbos)
+                       fprintf(output_file,
+                                       "     PID  S(CODE)  S(DATA)  P(CODE)  P(DATA)"
+                                       "     PEAK      PSS       3D"
+                                       "     GEM(PSS)  GEM(RSS)"
+                                       " OOM_SCORE_ADJ    COMMAND\n");
+               else
+                       fprintf(output_file,
+                                       "     PID     CODE     DATA     PEAK     PSS"
+                                       "     3D      GEM(PSS)      COMMAND\n");
+       }
+
+       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;
+
+               milist = load_maps(pid);
+               if (milist == 0)
+                       continue;
+
+               /* get classified map info */
+               get_trib_mapinfo(pid, milist, glist, &tmi);
+               oom_score_adj = get_oomscoreadj(pid);
+
+               if (!sum) {
+                       if (verbos)
+                               fprintf(output_file,
+                                       "%8d %8d %8d %8d %8d %8d %8d %8d %8d %8d"
+                                       " %8d \t\t%s\n",
+                                       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, oom_score_adj, cmdline);
+                       else
+                               fprintf(output_file,
+                                       "%8d %8d %8d %8d %8d %8d %8d      %s\n",
+                                       pid,
+                                       tmi.shared_clean +
+                                       tmi.private_clean,
+                                       tmi.shared_dirty + tmi.private_dirty,
+                                       tmi.peak_rss,
+                                       tmi.pss,
+                                       tmi.graphic_3d,
+                                       tmi.gem_pss, cmdline);
+
+                       if (tmi.other_devices != 0)
+                               fprintf(output_file,
+                                       "%s(%d) %d KB may mapped by device(s).\n",
+                                       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_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 = KB(total_gem_memory());
+       fprintf(output_file,
+                       "==============================================="
+                       "===============================================\n");
+       if (verbos) {
+               fprintf(output_file,
+                               "TOTAL:      S(CODE) S(DATA) P(CODE)  P(DATA)"
+                               "    PEAK     PSS       3D    "
+                               "GEM(PSS) GEM(RSS) GEM(ALLOC) TOTAL(KB)\n");
+               fprintf(output_file,
+                       "         %8d %8d %8d %8d %8d %8d %8d"
+                       " %8d %8d %8d %8d\n",
+                       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_pss + total_graphic_3d +
+                       total_allocated_gem);
+       } else {
+               fprintf(output_file,
+                       "TOTAL:        CODE     DATA    PEAK     PSS     "
+                       "3D    GEM(PSS) GEM(ALLOC)     TOTAL(KB)\n");
+               fprintf(output_file, "         %8d %8d %8d %8d %8d %8d %7d %8d\n",
+                       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_pss + total_graphic_3d +
+                       total_allocated_gem);
+
+       }
+
+       if (verbos)
+               fprintf(output_file,
+                       "* 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) \n");
+       else
+               fprintf(output_file,
+                       "* 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)\n");
+
+       get_tmpfs_info(output_file);
+       get_mem_info(output_file);
+
+       fclose(output_file);
+       free(glist);
+       closedir(pDir);
+       return 1;
+}
+
+static int show_map_new(int pid)
+{
+       mapinfo *milist;
+       mapinfo *mi;
+       unsigned shared_dirty = 0;
+       unsigned shared_clean = 0;
+       unsigned private_dirty = 0;
+       unsigned private_clean = 0;
+       unsigned pss = 0;
+       unsigned start = 0;
+       unsigned end = 0;
+       unsigned private_clean_total = 0;
+       unsigned private_dirty_total = 0;
+       unsigned shared_clean_total = 0;
+       unsigned shared_dirty_total = 0;
+       int duplication = 0;
+
+       milist = load_maps(pid);
+
+       if (milist == 0) {
+               fprintf(stderr, "cannot get /proc/smaps for pid %d\n", pid);
+               return 1;
+       }
+
+       if (!sum) {
+               printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  ADDR(start-end)"
+                       "OBJECT NAME\n");
+               printf("-------- -------- -------- -------- -----------------"
+                       "------------------------------\n");
+       } else {
+               printf(" S(CODE)  S(DATA)  P(CODE)  P(DATA)  PSS\n");
+               printf("-------- -------- -------------------"
+                       "------------------\n");
+       }
+       for (mi = milist; mi; mi = mi->next) {
+               shared_clean += mi->shared_clean;
+               shared_dirty += mi->shared_dirty;
+               private_clean += mi->private_clean;
+               private_dirty += mi->private_dirty;
+               pss += mi->pss;
+
+               shared_clean_total += mi->shared_clean;
+               shared_dirty_total += mi->shared_dirty;
+               private_clean_total += mi->private_clean;
+               private_dirty_total += mi->private_dirty;
+
+               if (!duplication)
+                       start = mi->start;
+
+               if ((mi->next && !strcmp(mi->next->name, mi->name)) &&
+                   (mi->next->start == mi->end)) {
+                       duplication = 1;
+                       continue;
+               }
+               end = mi->end;
+               duplication = 0;
+
+               if (!sum) {
+                       printf("%8d %8d %8d %8d %08x-%08x %s\n",
+                              shared_clean, shared_dirty, private_clean, private_dirty,
+                              start, end, mi->name);
+               }
+               shared_clean = 0;
+               shared_dirty = 0;
+               private_clean = 0;
+               private_dirty = 0;
+       }
+       if (sum) {
+               printf("%8d %8d %8d %8d %18d\n",
+                      shared_clean_total,
+                      shared_dirty_total,
+                      private_clean_total,
+                      private_dirty_total,
+                      pss);
+       }
+
+       return 1;
+}
+
+void check_kernel_version(void)
+{
+       struct utsname buf;
+       int ret;
+
+       ret = uname(&buf);
+
+       if (!ret) {
+               if (buf.release[0] == '3') {
+                       char *pch;
+                       char str[3];
+                       int sub_version;
+                       pch = strstr(buf.release, ".");
+                       strncpy(str, pch+1, 2);
+                       sub_version = atoi(str);
+
+                       if (sub_version >= 10)
+                               ignore_smaps_field = 8; /* Referenced, Anonymous, AnonHugePages,
+                                                  Swap, KernelPageSize, MMUPageSize,
+                                                  Locked, VmFlags */
+
+                       else
+                               ignore_smaps_field = 7; /* Referenced, Anonymous, AnonHugePages,
+                                                  Swap, KernelPageSize, MMUPageSize,
+                                                  Locked */
+               } else {
+                       ignore_smaps_field = 4; /* Referenced, Swap, KernelPageSize,
+                                                  MMUPageSize */
+               }
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       int usage = 1;
+       sum = 0;
+
+       if (argc > 1) {
+               check_kernel_version();
+
+               if (!strcmp(argv[1], "-r")) {
+                       if (argc >= 3)
+                               show_rss(OUTPUT_FILE, argv[2]);
+                       else
+                               show_rss(OUTPUT_UART, NULL);
+                       usage = 0;
+               } else if (!strcmp(argv[1], "-s")) {
+                       sum = 1;
+                       if (argc == 3 && atoi(argv[2]) > 0) {
+                               show_map_new(atoi(argv[2]));
+                               usage = 0;
+                       }
+               } else if (!strcmp(argv[1], "-a")) {
+                       verbos = 0;
+                       show_map_all_new(OUTPUT_UART, NULL);
+                       usage = 0;
+               } else if (!strcmp(argv[1], "-v")) {
+                       verbos = 1;
+                       show_map_all_new(OUTPUT_UART, NULL);
+                       usage = 0;
+               } else if (!strcmp(argv[1], "-f")) {
+                       if (argc >= 3) {
+                               verbos = 1;
+                               show_map_all_new(OUTPUT_FILE, argv[2]);
+                               usage = 0;
+                       }
+               } else if (argc == 2 && atoi(argv[1]) > 0) {
+                       show_map_new(atoi(argv[1]));
+                       usage = 0;
+               }
+       }
+       if (usage) {
+               fprintf(stderr,
+                       "memps [-a] | [-v] | [-s] <pid> | [-f] <output file full path>\n"
+                       "        -s = sum (show only sum of each)\n"
+                       "        -f = all (show all processes via output file)\n"
+                       "        -a = all (show all processes)\n"
+                       "        -v = verbos (show all processes in detail)\n");
+       }
+
+       return 0;
+}
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..9d76d87
--- /dev/null
@@ -0,0 +1,247 @@
+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)
+       ADD_LIBRARY(${PROJECT} SHARED ${SOURCES})
+       TARGET_LINK_LIBRARIES(${PROJECT} ${shared_pkgs_LDFLAGS})
+
+       SET_TARGET_PROPERTIES(${PROJECT}
+                       PROPERTIES
+                       VERSION ${FULLVER}
+                       SOVERSION ${MAJORVER}
+                       CLEAN_DIRECT_OUTPUT 1
+       )
+
+       INSTALL(TARGETS ${PROJECT} DESTINATION lib)
+
+       RETURN()
+ENDIF()
+
+INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR}
+               ${NETWORK_SOURCE_DIR}/include
+               ${RESOURCED_INCLUDEDIR}
+       )
+
+SET (REQUIRES_LIST dlog
+                  glib-2.0
+                  sqlite3
+                  vconf
+                  ecore
+                  edbus
+)
+
+IF("${TETHERING_FEATURE}" STREQUAL "ON")
+       SET(REQUIRES_LIST ${REQUIRES_LIST}
+                  capi-network-connection)
+ENDIF()
+
+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 -Wall -Werror")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+
+IF("${ARCH}" STREQUAL "arm")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+IF("${NETWORK_SERVICE}" STREQUAL "OFF")
+       ADD_DEFINITIONS("-DNETWORK_SERVICE_OFF")
+ENDIF()
+
+#### 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(protocol-info_HEADERS
+  ${NETWORK_SOURCE_DIR}/include/protocol-info.h)
+
+SET(protocol-info_SOURCES
+  ${NETWORK_SOURCE_DIR}/protocol-info.c)
+
+SET(quota_HEADERS
+  ${NETWORK_SOURCE_DIR}/include/datausage-quota-processing.h)
+
+SET(quota_SOURCES
+  ${COMMON_SOURCE_DIR}/edbus-handler.c
+  ${NETWORK_SOURCE_DIR}/datausage-quota.c
+  )
+
+SET(app-stat_HEADERS
+  ${INCLUDE_COMMON_DIR}/app-stat.h)
+
+SET(app-stat_SOURCES
+  ${NETWORK_SOURCE_DIR}/app-stat.c)
+
+SET(net-iface_HEADERS
+  ${NETWORK_SOURCE_DIR}/include/iface.h)
+
+SET(net-iface_SOURCES
+  ${NETWORK_SOURCE_DIR}/iface.c
+)
+
+SET(roaming_HEADERS
+  ${NETWORK_SOURCE_DIR}/include/roaming.h)
+
+SET(roaming_SOURCES
+  ${NETWORK_SOURCE_DIR}/dummy_roaming.c)
+
+ADD_LIBRARY(protocol-info STATIC
+  ${protocol-info_SOURCES} ${protocol-info_HEADERS})
+
+ADD_LIBRARY(storage STATIC
+  ${storage_SOURCES} ${storage_HEADERS}
+  ${quota_SOURCES} ${quota_HEADERS}
+  )
+TARGET_LINK_LIBRARIES(storage protocol-info ${pkgs_${NETWORK}_LDFLAGS})
+
+ADD_LIBRARY(app-stat STATIC
+  ${app-stat_SOURCES} ${app-stat_HEADERS})
+TARGET_LINK_LIBRARIES(app-stat net-cls)
+
+ADD_LIBRARY(net-iface STATIC
+  ${net-iface_SOURCES})
+
+ADD_LIBRARY(roaming STATIC
+  ${roaming_SOURCES})
+TARGET_LINK_LIBRARIES(roaming ${pkgs_${NETWORK}_LDFLAGS})
+
+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}/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.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
+  ${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")
+
+INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR}
+                   ${NETWORK_SOURCE_DIR}/include
+                   ${RESOURCED_SOURCE_DIR})
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+ADD_LIBRARY(${PROJECT} SHARED
+  ${${PROJECT}_HEADERS}
+  ${${PROJECT}_SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROJECT} roaming settings net-cls
+  ${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}
+                     ${COMMON_SOURCE_DIR}/config-parser.c
+                     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 ####
+
+#### rd-network ####
+SET(SOURCES
+       ${NETWORK_SOURCE_DIR}/network-dummy.c)
+
+ADD_LIBRARY(${NETWORK} SHARED ${SOURCES})
+ADD_DEPENDENCIES(${NETWORK} ${PROJECT})
+TARGET_LINK_LIBRARIES(${NETWORK} ${${NETWORK}_LDFLAGS} -L${LIBRARY_OUTPUT_PATH} -l${PROJECT})
+
+SET_TARGET_PROPERTIES(${NETWORK}
+     PROPERTIES
+     VERSION ${FULLVER}
+     SOVERSION ${MAJORVER}
+     CLEAN_DIRECT_OUTPUT 1
+)
+
+INSTALL(TARGETS ${NETWORK} DESTINATION lib)
+INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/rd-network.h DESTINATION include/system)
+
+#### rd-network ####
diff --git a/src/network/app-stat.c b/src/network/app-stat.c
new file mode 100644 (file)
index 0000000..7d114c8
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "app-stat.h"
+#include "net-cls-cgroup.h"
+#include "iface.h"
+#include "macro.h"
+#include "roaming.h"
+#include "trace.h"
+
+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 = 0;
+
+       ret = a_key->classid - b_key->classid;
+       if (!ret)
+               return ret;
+
+       ret = a_key->iftype - b_key->iftype;
+       if (!ret)
+               return ret;
+
+       return strcmp(a_key->ifname, b_key->ifname);
+}
+
+static void free_stat(gpointer data)
+{
+       free(data);
+}
+
+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_stat,
+                                                        free_app);
+       app_stat_tree->last_touch_time = time(0);
+       ret = pthread_rwlock_init(&app_stat_tree->guard, NULL);
+       if (ret != 0) {
+               strerror_r(ret, buf, sizeof(buf));
+               _E("Could not initialize tree guard %s.", 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;
+}
+
+traffic_stat_tree *create_traffic_stat_tree(void)
+{
+       return g_tree_new_full(compare_classid, NULL, NULL, free_stat);
+}
+
+void free_traffic_stat_tree(traffic_stat_tree *tree)
+{
+       g_tree_destroy((GTree *) tree);
+}
+
+static gboolean set_app_id(gpointer key, gpointer value,
+       void __attribute__((__unused__)) *data)
+{
+       /* 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);
+}
+
+static gboolean fill_incomming(gpointer key,
+       gpointer value, gpointer data)
+{
+       struct application_stat_tree *app_tree =
+               (struct application_stat_tree *)data;
+       struct traffic_stat *in_event = (struct traffic_stat *)value;
+
+       struct application_stat *app_stat = NULL;
+       if (!is_allowed_ifindex(in_event->ifindex))
+               return FALSE;
+
+       app_stat = (struct application_stat *)
+               g_tree_lookup((GTree *)app_tree->tree, key);
+       if (app_stat)
+               app_stat->rcv_count += in_event->bytes;
+       else {
+               app_stat = g_new(struct application_stat, 1);
+               memset(app_stat, 0, sizeof(struct application_stat));
+               app_stat->rcv_count = in_event->bytes;
+               g_tree_insert((GTree *)app_tree->tree, key, app_stat);
+       }
+       app_stat->delta_rcv += in_event->bytes;
+
+       /*only for debug purpose*/
+       if (!app_stat->ifindex)
+               app_stat->ifindex = in_event->ifindex;
+
+       app_stat->is_roaming = get_roaming();
+       return FALSE;
+}
+
+static gboolean fill_outgoing(gpointer key,
+       gpointer value, gpointer data)
+{
+       struct application_stat_tree *app_tree =
+               (struct application_stat_tree *)data;
+       struct traffic_stat *out_event = (struct traffic_stat *)value;
+
+       struct application_stat *app_stat = (struct application_stat *)
+               g_tree_lookup((GTree *)app_tree->tree, key);
+       if (app_stat)
+               app_stat->snd_count += out_event->bytes;
+       else {
+               app_stat = g_new(struct application_stat, 1);
+               memset(app_stat, 0, sizeof(struct application_stat));
+               app_stat->snd_count = out_event->bytes;
+               g_tree_insert((GTree *)app_tree->tree, key, app_stat);
+       }
+       app_stat->delta_snd += out_event->bytes;
+
+       if (!app_stat->ifindex)
+               app_stat->ifindex = out_event->ifindex;
+
+       if (!app_stat->is_roaming)
+               app_stat->is_roaming = get_roaming();
+       return FALSE;
+}
+
+
+static void fill_result(traffic_stat_tree *tree_in,
+               traffic_stat_tree *tree_out,
+               struct application_stat_tree *result)
+{
+
+       g_tree_foreach(tree_in, (GTraverseFunc)fill_incomming, result);
+       g_tree_foreach(tree_out, (GTraverseFunc)fill_outgoing, result);
+}
+
+resourced_ret_c prepare_application_stat(traffic_stat_tree *tree_in,
+                            traffic_stat_tree *tree_out,
+                            struct application_stat_tree *result,
+                            volatile struct daemon_opts *opts)
+{
+       fill_result(tree_in, tree_out, result);
+       identify_application(result);
+
+       return RESOURCED_ERROR_NONE;
+}
diff --git a/src/network/counter-process.c b/src/network/counter-process.c
new file mode 100644 (file)
index 0000000..1f90d39
--- /dev/null
@@ -0,0 +1,1246 @@
+/*
+ * 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 "roaming.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) " \
+       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"
+#define REMOVE_QUOTA "DELETE FROM quotas WHERE binpath=? AND iftype=? " \
+       " AND roaming=?"
+
+#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)
+{
+       return nfacct_send_get(carg) == 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;
+       }
+
+       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);
+
+       keep_counter(&counter);
+}
+
+static void populate_traf_stat_list(char *cnt_name, uint64_t bytes,
+                       struct counter_arg *carg)
+{
+       struct traffic_stat *to_insert;
+       struct classid_iftype_key *key;
+       struct nfacct_rule counter = { .name = {0}, .ifname = {0}, 0, };
+       traffic_stat_tree *tree = NULL;
+
+       _D("cnt_name %s", cnt_name);
+
+       if (!recreate_counter_by_name(cnt_name, &counter)) {
+               _E("Can't parse counter name %s", cnt_name);
+               return;
+       }
+
+       _D("classid %u, iftype %u, iotype %d, intend %d, ifname %s, bytes %lu",
+          counter.classid, counter.iftype, counter.iotype, counter.intend, counter.ifname, bytes);
+
+       if (counter.iotype == NFACCT_COUNTER_UNKNOWN ||
+               counter.intend != NFACCT_COUNTER) {
+               _E("Counter type is not supported!");
+               return;
+       }
+
+       tree = counter.iotype == NFACCT_COUNTER_IN ? carg->in_tree : carg->out_tree;
+       to_insert = g_new(struct traffic_stat, 1);
+       if (!to_insert) {
+               _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat));
+               return;
+       }
+
+       key = g_new(struct classid_iftype_key, 1);
+
+       if (!key) {
+               _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key));
+               g_free((gpointer)to_insert);
+               return;
+       }
+
+       to_insert->bytes = bytes;
+       /*to_insert->ifindex = cur->ifindex;*/
+       key->classid = counter.classid;
+       key->iftype = counter.iftype;
+       STRING_SAVE_COPY(key->ifname, counter.ifname);
+       g_tree_insert((GTree *) tree, (gpointer)key, to_insert);
+}
+
+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)
+                       populate_traf_stat_list(cnt_name, bytes, 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;
+
+       if (check_net_blocked(carg->opts->state)) {
+               ecore_timer_freeze(carg->ecore_timer);
+               return ECORE_CALLBACK_RENEW;
+       }
+
+       if (!(carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)) {
+               /* Here we just sent command,
+                * answer we receiving in another callback, send_command uses
+                * return value the same as sendto
+                */
+
+               return send_counter_request(carg);
+       }
+
+       close(carg->sock);
+       return ECORE_CALLBACK_CANCEL;
+}
+
+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_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;
+       enum traffic_restriction_type rst_type;
+
+       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;
+       }
+
+       dbus_ret = proc_keep_restriction(appid, NONE_QUOTA_ID, &rest,
+                                            rst_type);
+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 (!(m_data->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE))
+                       m_data->carg->opts->state |=
+                               RESOURCED_FORCIBLY_FLUSH_STATE;
+
+               /* 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;
+}
+
+struct serialization_quota {
+       int time_period;
+       int64_t snd_quota;
+       int64_t rcv_quota;
+       int snd_warning_threshold;
+       int rcv_warning_threshold;
+       resourced_state_t quota_type;
+       resourced_iface_type iftype;
+       time_t start_time;
+       resourced_roaming_type roaming_type;
+};
+
+static inline int _get_threshold_part(int time_period)
+{
+       if (time_period < RESOURCED_PERIOD_DAY)
+               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, &quota->time_period,
+               DBUS_TYPE_UINT64, &quota->snd_quota,
+               DBUS_TYPE_UINT64, &quota->rcv_quota,
+               DBUS_TYPE_INT32, &quota->snd_warning_threshold,
+               DBUS_TYPE_INT32, &quota->rcv_warning_threshold,
+               DBUS_TYPE_INT32, &quota->quota_type,
+               DBUS_TYPE_INT32, &quota->iftype,
+               DBUS_TYPE_INT32, &quota->start_time,
+               DBUS_TYPE_INT32, &quota->roaming_type,
+               DBUS_TYPE_INVALID);
+       if (ret == FALSE) {
+               _E("Can't deserialize set quota message ![%s:%s]\n",
+               err.name, err.message);
+       }
+
+       dbus_error_free(&err);
+
+       quota->iftype = (quota->iftype == RESOURCED_IFACE_UNKNOWN) ?
+                       RESOURCED_IFACE_ALL : quota->iftype;
+
+       quota->snd_warning_threshold = _evaluate_warning_threshold(
+               quota->snd_quota, quota->time_period,
+               quota->snd_warning_threshold);
+       quota->rcv_warning_threshold = _evaluate_warning_threshold(
+               quota->rcv_quota, quota->time_period,
+               quota->rcv_warning_threshold);
+
+return ret;
+}
+
+static dbus_bool_t deserialize_remove_quota(
+       DBusMessage *msg, char **appid,
+       resourced_iface_type *iftype, resourced_roaming_type *roaming)
+{
+       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_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)
+{
+       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 iftype:%d for preparing statement",
+                       roaming);
+               error_code =  RESOURCED_ERROR_DB_FAILED;
+               goto out;
+       }
+
+       if (sqlite3_step(datausage_quota_remove) != SQLITE_DONE) {
+               _E("failed to remove record");
+               error_code =  RESOURCED_ERROR_DB_FAILED;
+               goto out;
+       }
+
+       restriction_set_status(RESTRICTION_STATE_UNSET);
+
+       _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;
+       resourced_iface_type iftype;
+       resourced_ret_c ret = RESOURCED_ERROR_NONE;
+       resourced_roaming_type roaming;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       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)
+                            == FALSE) {
+               ret = RESOURCED_ERROR_INVALID_PARAMETER;
+               goto remove_out;
+       }
+
+       ret = remove_quota(app_id, iftype, roaming);
+       update_quota_state(app_id, iftype, 0, 0, roaming);
+
+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)
+{
+       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_STATIC) != 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 start_time: %d for preparing statement",
+                       quota->start_time);
+               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;
+       }
+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;
+       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;
+       }
+
+       deserialize_quota(msg, &app_id, &quota);
+       ret = store_quota(app_id, &quota);
+       if (ret != RESOURCED_ERROR_NONE) {
+               _E("Can't store quota!");
+               goto update_out;
+       }
+
+       update_quota_state(app_id, quota.iftype, quota.start_time,
+               quota.time_period, quota.roaming_type);
+
+       ret_value_msg_if(!carg->opts,
+               dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS,
+                                     "Counter args is not provided"),
+                        "Please provide valid argument!");
+
+       carg->opts->is_update_quota = 1;
+       reschedule_count_timer(carg, 0);
+       _SD("Datausage quota changed");
+
+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->foreground.cnt.incoming_bytes);
+               /* outgoing bytes */
+               dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &info->foreground.cnt.outgoing_bytes);
+
+               dbus_message_iter_append_basic(&sub, DBUS_TYPE_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;
+       }
+
+       _SD("Datausage get stats");
+       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 RESOURCED_ALL_APP;
+
+       appid = get_app_id_by_classid(classid, true);
+       return !appid ? UNKNOWN_APP : appid;
+}
+
+static bool need_flush_immediatelly(sig_atomic_t state)
+{
+       return state & RESOURCED_FORCIBLY_FLUSH_STATE ||
+               state & RESOURCED_FORCIBLY_QUIT_STATE;
+}
+
+static Eina_Bool _store_and_free_result_cb(void *user_data)
+{
+       struct counter_arg *arg = (struct counter_arg *)user_data;
+
+       ret_value_msg_if(!arg, ECORE_CALLBACK_CANCEL, "Please provide valid argument!");
+
+       if (store_result(arg->result, need_flush_immediatelly(arg->opts->state)
+                        ? 0 : arg->opts->flush_period)) {
+               /*We still plan to use result outside, just
+               remove and free elements */
+               g_tree_ref(arg->result->tree);
+               free_app_stat_tree(arg->result);
+               if (arg->opts->state & RESOURCED_FORCIBLY_FLUSH_STATE) {
+                       arg->opts->state &= ~RESOURCED_FORCIBLY_FLUSH_STATE;
+                       if (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->store_result_timer = NULL;
+       return ECORE_CALLBACK_CANCEL;
+}
+
+static void _store_and_free_result(struct counter_arg *arg)
+{
+       if (!arg->store_result_timer)
+               arg->store_result_timer = ecore_timer_add(STORE_DELAY_INTERVAL,
+                                          _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));
+
+       /* process only filled in/out or tethering traffic */
+       if ((!g_tree_nnodes(params->carg->in_tree) ||
+            !g_tree_nnodes(params->carg->out_tree)) &&
+           params->carg->opts->state & RESOURCED_FORCIBLY_QUIT_STATE)
+               return;
+
+       pthread_rwlock_wrlock(&params->carg->result->guard);
+       ret = prepare_application_stat(params->carg->in_tree,
+                       params->carg->out_tree, params->carg->result,
+               params->carg->opts);
+       pthread_rwlock_unlock(&params->carg->result->guard);
+
+       if (ret != RESOURCED_ERROR_NONE) {
+               _E("Failed to prepare application statistics!");
+               return;
+       }
+       ret = process_quota(params->carg->result, params->carg->opts);
+       if (ret != 0) {
+               _E("Failed to process quota!");
+               return;
+       }
+
+       _store_and_free_result(params->carg);
+
+       g_tree_ref(params->carg->out_tree);
+       free_traffic_stat_tree(params->carg->out_tree);
+       g_tree_ref(params->carg->in_tree);
+       free_traffic_stat_tree(params->carg->in_tree);
+}
+
+#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,};
+       resourced_ret_c ret;
+
+       _D("Restriction notification");
+
+       if (process_netlink_restriction_msg(cmd->ans, &restriction,
+           &notification_type) !=
+           RESOURCED_ERROR_NONE) {
+               _E("Failed to process netlink restriction.");
+               return;
+       }
+
+       app_id = _get_public_appid(restriction.sk_classid);
+       iftype = get_iftype(restriction.ifindex);
+
+       ret = get_restriction_info(app_id, iftype, &rst_info);
+       ret_msg_if(ret != RESOURCED_ERROR_NONE,
+               "Failed to get restriction info!");
+
+       if (notification_type == RESTRICTION_NOTI_C_ACTIVE) {
+               if (rst_info.quota_id != NONE_QUOTA_ID)
+                       send_restriction_notification(app_id);
+               update_restriction_db(app_id, iftype, 0, 0,
+                                     RESOURCED_RESTRICTION_ACTIVATED,
+               rst_info.quota_id, rst_info.roaming);
+       } 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);
+       } else
+               _E("Unkown restriction notification type");
+}
+
+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();
+       carg->in_tree = create_traffic_stat_tree();
+       carg->out_tree = create_traffic_stat_tree();
+#ifdef CONFIG_DATAUSAGE_NFACCT
+       carg->nf_cntrs = create_nfacct_tree();
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+       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");
+       free_traffic_stat_tree(carg->out_tree);
+       free_traffic_stat_tree(carg->in_tree);
+       nulify_app_stat_tree(&carg->result);
+       ecore_main_fd_handler_del(carg->ecore_fd_handler);
+       ecore_timer_del(carg->ecore_timer);
+       finalize_quota_insert();
+       finalize_quota_remove();
+}
diff --git a/src/network/counter.c b/src/network/counter.c
new file mode 100644 (file)
index 0000000..fd436a3
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ *  @file: 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 daemon_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;
+       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));
+}
diff --git a/src/network/daemon-options.c b/src/network/daemon-options.c
new file mode 100644 (file)
index 0000000..a610026
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 "daemon-options.h"
+#include "macro.h"
+#include "resourced.h"
+#include "settings.h"
+#include "trace.h"
+
+void load_daemon_opts(struct daemon_opts *daemon_options)
+{
+       resourced_options options = { 0 };
+
+       ret_msg_if(daemon_options == NULL,
+                        "Invalid daemon options argument\n");
+       load_options(&options);
+       daemon_options->datacall_logging = options.datacall_logging;
+       daemon_options->update_period = options.datausage_timer;
+}
diff --git a/src/network/datausage-common.c b/src/network/datausage-common.c
new file mode 100644 (file)
index 0000000..b081568
--- /dev/null
@@ -0,0 +1,965 @@
+/*
+ * 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.c
+ *
+ * @desc Datausage module
+ *
+ */
+
+#include "appid-helper.h"
+#include "config.h"
+#include "const.h"
+#include "counter-process.h"
+#include "counter.h"
+#include "cgroup.h"
+#include "datausage-restriction.h"
+#include "generic-netlink.h"
+#include "net-cls-cgroup.h"
+#include "nl-helper.h"
+#include "notifier.h"
+#include "notification.h" /* for sending datausage dbus notification */
+#include "daemon-options.h"
+#include "datausage-common.h"
+#include "datausage-quota.h"
+#include "datausage-vconf-callbacks.h"
+#include "iface-cb.h"
+#include "macro.h"
+#include "module-data.h"
+#include "module.h"
+#include "nfacct-rule.h"
+#include "protocol-info.h"
+#include "resourced.h"
+#include "restriction-handler.h"
+#include "roaming.h"
+#include "storage.h"
+#include "trace.h"
+
+#include <linux/rtnetlink.h>
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+
+
+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_NAME_LENGTH];
+};
+
+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 */
+};
+
+struct nfacct_value {
+       pid_t pid;
+       enum nfacct_state state;
+};
+
+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 void resourced_roaming_cb_init(void)
+{
+       regist_roaming_cb(get_roaming_restriction_cb());
+}
+
+static int app_launch_cb(void *data)
+{
+       struct proc_status *p_data = (struct proc_status*)data;
+       int ret;
+       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;
+}
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+
+static int remove_each_counter(
+       gpointer key,
+       gpointer value,
+       gpointer data)
+{
+       struct nfacct_rule *counter = (struct nfacct_rule *)data;
+       resourced_iface_type iftype = *(resourced_iface_type *)value;
+       struct nfacct_key nf_key;
+
+       if (iftype == RESOURCED_IFACE_UNKNOWN)
+               return FALSE;
+
+       nf_key.classid = counter->classid;
+       nf_key.iotype = counter->iotype;
+       counter->iftype = nf_key.iftype = iftype;
+
+       generate_counter_name(counter);
+       counter->iptables_rule(counter);
+
+       /*  remove from local tree  */
+#ifdef DEBUG_ENABLED
+       {
+               gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key);
+               if (t)
+                       _I("Element exists, remove it!");
+               else
+                       _D("Element doesn't exist!");
+       }
+#endif
+
+       g_tree_remove(counter->carg->nf_cntrs, &nf_key);
+#ifdef DEBUG_ENABLED
+       {
+               gconstpointer t = g_tree_lookup(counter->carg->nf_cntrs, &nf_key);
+               if (t)
+                       _E("Element wasn't removed!");
+       }
+#endif
+
+       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,
+               /* .name until we don't have iftype,
+               *       we couldn't get name */
+       };
+
+       /* TODO rework for_each_ifindex to avoid cast,
+        * right now cast is necessary due for_each_ifindex directy pass
+        * given function into g_tree_foreach */
+       /* remove for ingress counter */
+       for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter);
+       /* remove for engress counter */
+       counter.iotype = NFACCT_COUNTER_OUT;
+       counter.iptables_rule = del_iptables_out;
+       for_each_ifindex((ifindex_iterator)remove_each_counter, NULL, &counter);
+}
+
+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;
+}
+
+static int app_terminate_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;
+       ret_value_msg_if(p_data == NULL, RESOURCED_ERROR_FAIL,
+               "Please provide valid argument!");
+
+       m_data = get_shared_modules_data();
+       ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
+               "Can't get module data!");
+
+       carg = m_data->carg;
+       ret_value_msg_if(carg == NULL, RESOURCED_ERROR_FAIL,
+               "Cant' get counter arg!");
+       classid = get_classid_by_pid(carg, p_data->pid);
+       ret_value_msg_if(classid == RESOURCED_UNKNOWN_CLASSID,
+               RESOURCED_ERROR_FAIL, "No classid to terminate!");
+
+       remove_nfacct_counters_for_all_iface(classid, carg);
+       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 *)value;
+
+       if (iftype <= RESOURCED_IFACE_UNKNOWN ||
+               iftype >= RESOURCED_IFACE_LAST_ELEM)
+               return FALSE;
+
+       counter.iotype = NFACCT_COUNTER_IN;
+       counter.iftype = iftype;
+       counter.carg = carg;
+       generate_counter_name(&counter);
+       add_iptables_in(&counter);
+       counter.iotype = NFACCT_COUNTER_OUT;
+       generate_counter_name(&counter);
+       add_iptables_out(&counter);
+       return FALSE;
+}
+
+static void add_tizen_os_counters(struct counter_arg *carg) {
+
+       for_each_ifindex((ifindex_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);
+}
+
+/* notification section */
+/*
+ * 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 RESOURCED_ALL_APP;
+
+       appid = get_app_id_by_classid(classid, true);
+       return !appid ? 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;
+       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 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]);
+       char *app_id = 0;
+       int ret = 0;
+       resourced_restriction_info rst_info = {0};
+
+       init_nfacct(0, 0, 0, carg, &counter);
+       strncpy(counter.name, cnt_name, sizeof(counter.name)-1);
+       recreate_counter_by_name(cnt_name, &counter);
+
+       app_id = get_public_appid(counter.classid);
+       ret = get_restriction_info(app_id, counter.iftype, &rst_info);
+        ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                "Failed to get restriction info!");
+
+       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);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "Can't delete restriction%s", counter.name);
+
+                       out_counter.iotype = NFACCT_COUNTER_OUT;
+                       generate_counter_name(&out_counter);
+                       ret = add_iptables_out(&out_counter);
+                       /* TODO need to think how to release it and what about
+                        * not yet fired rule */
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "Can't create auxilary counter %s", out_counter.name);
+               }
+
+               if (rst_info.quota_id != NONE_QUOTA_ID)
+                       send_restriction_notification(app_id);
+               update_restriction_db(app_id, counter.iftype, 0, 0,
+                                     RESOURCED_RESTRICTION_ACTIVATED,
+               rst_info.quota_id, rst_info.roaming);
+
+       } else if (counter.intend == NFACCT_WARN) {
+               if (rst_info.quota_id != NONE_QUOTA_ID)
+                       send_restriction_warn_notification(app_id);
+               /* remove both warnings */
+               counter.iotype = NFACCT_COUNTER_IN | NFACCT_COUNTER_OUT;
+               ret = del_counter(&counter);
+               ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                       "Can't delete warning %s", counter.name);
+       } else
+               _E("Unkown restriction notification type");
+
+       return 0;
+}
+
+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_terminate_cb(void *data)
+{
+       return 0;
+}
+
+iface_callback *create_counter_callback(void)
+{
+       return NULL;
+}
+
+#endif /* CONFIG_DATAUSAGE_NFACCT */
+
+static int resourced_datausage_init(void *data)
+{
+       struct modules_arg *marg = (struct modules_arg *)data;
+       struct shared_modules_data *m_data = get_shared_modules_data();
+       int ret_code;
+
+       load_daemon_opts(marg->opts);
+       _D("Initialize network counter function\n");
+       ret_value_msg_if(marg == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "Invalid modules argument\n");
+       ret_value_msg_if(m_data == NULL, RESOURCED_ERROR_FAIL,
+                        "Invalid shared modules data\n");
+       /* register notifier cb */
+       register_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
+       register_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb);
+       register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb);
+       register_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb);
+       m_data->carg = init_counter_arg(marg->opts);
+       ret_code = resourced_iface_init();
+       ret_value_msg_if(ret_code < 0, ret_code, "resourced_iface_init failed");
+       resourced_roaming_cb_init();
+       ret_code = resourced_init_counter_func(m_data->carg);
+       ret_value_msg_if(ret_code < 0, ret_code, "Error init counter func\n");
+       resourced_add_vconf_datausage_cb(m_data->carg);
+       init_hw_net_protocol_type();
+       reactivate_restrictions();
+
+#ifdef CONFIG_DATAUSAGE_NFACCT
+       reload_all_nf_counters(m_data->carg);
+
+       /* let's make a notification socket */
+       init_notifier(m_data->carg);
+#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);
+       finalize_carg(m_data->carg);
+       finalize_storage_stm();
+       finalize_hw_net_protocol_type();
+       unregister_notifier(RESOURCED_NOTIFIER_APP_LAUNCH, app_launch_cb);
+       unregister_notifier(RESOURCED_NOTIFIER_APP_RESUME, app_launch_cb);
+       unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, app_launch_cb);
+       unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATE, app_terminate_cb);
+       resourced_iface_finalize();
+       finalize_iftypes();
+
+       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;
+       return strcmp(key_a->ifname, key_b->ifname);
+}
+
+GTree *create_nfacct_tree(void)
+{
+       return g_tree_new_full(compare_nfcntr, NULL, NULL, free);
+}
+
+static struct nfacct_value *lookup_counter(struct nfacct_rule *counter)
+{
+       struct nfacct_key key = {
+               .classid = counter->classid,
+               .iftype = counter->iftype,
+               .iotype = counter->iotype
+       };
+       STRING_SAVE_COPY(key.ifname, counter->ifname);
+
+       return (struct nfacct_value *)g_tree_lookup(counter->carg->nf_cntrs,
+               &key);
+}
+
+/* Called only in case of successful kernle operation */
+void keep_counter(struct nfacct_rule *counter)
+{
+       struct nfacct_key *key = NULL;
+       struct nfacct_value *value = NULL;
+
+       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;
+       STRING_SAVE_COPY(key->ifname, counter->ifname);
+
+       value->pid =  counter->pid;
+       value->state = NFACCT_STATE_ACTIVE;
+
+       g_tree_insert(counter->carg->nf_cntrs, key, value);
+}
+
+static int create_each_iptable_rule(gpointer key, gpointer value, void *data)
+{
+       struct make_rule_context *ctx = (struct make_rule_context *)data;
+       resourced_ret_c ret;
+       resourced_iface_type iftype = *(resourced_iface_type *)value;
+       struct nfacct_value *counter = NULL;
+
+       if (iftype <= RESOURCED_IFACE_UNKNOWN ||
+               iftype >= RESOURCED_IFACE_LAST_ELEM) {
+               _D("Unsupported network interface type %d",
+                       iftype);
+               return RESOURCED_ERROR_NONE;
+       }
+
+       ctx->counter->iftype = iftype;
+       generate_counter_name(ctx->counter);
+       counter = lookup_counter(ctx->counter);
+       if (counter != NULL) {
+               _D("Counter already exists!");
+               return RESOURCED_ERROR_NONE;
+       }
+       ret = ctx->counter->iptables_rule(ctx->counter);
+       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, RESOURCED_ERROR_FAIL,
+               "Can't add iptables ingress rule");
+
+       keep_counter(ctx->counter);
+       return RESOURCED_ERROR_NONE;
+}
+
+static void populate_incomplete_counter(void *data)
+{
+       struct make_rule_context *ctx = (struct make_rule_context *)data;
+       struct nfacct_value *counter;
+       generate_counter_name(ctx->counter);
+
+       counter = lookup_counter(ctx->counter);
+       if (counter != NULL) {
+               _D("Counter already exists!");
+               return;
+       }
+       keep_counter(ctx->counter);
+}
+
+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_ifindex((ifindex_iterator)create_each_iptable_rule,
+               populate_incomplete_counter, &ctx);
+
+       counter.iotype = NFACCT_COUNTER_OUT;
+       counter.iptables_rule = add_iptables_out;
+       for_each_ifindex((ifindex_iterator)create_each_iptable_rule,
+               populate_incomplete_counter, &ctx);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+/* iface reset section */
+struct iftype_context {
+       resourced_iface_type iftype;
+       struct counter_arg *carg;
+};
+
+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 */
+}
+
+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;
+
+       /* ugly check, due in case of RMNET -> WLAN switch,
+        *              WLAN activated before then RMNET is deactivated */
+
+       /*
+        * skip activating in case of
+        * 1. new interface is the same as was before
+        * 2. and counter is still active and new interface is Wifi
+        *      such problem was with WiFi only
+        * 3. and state is not deactivated, it's mean we wil skip in case of active
+        *   incomplete counter
+        * */
+       if (!(ctx->iftype != nfacct_key->iftype &&
+           nfacct_value->state == NFACCT_STATE_ACTIVE &&
+           ctx->iftype == RESOURCED_IFACE_WIFI) &&
+           nfacct_value->state != NFACCT_STATE_DEACTIVATED &&
+           !is_incomplete_counter(nfacct_key, nfacct_value))
+               /* it means ctx->iftype was activated, but we still have
+                *      active counter for another interface, assume
+                *      WLAN is preffered, so lets deactivate it */
+               return FALSE; /* continue iteration */
+
+
+       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!");
+
+       if (counter.iotype == NFACCT_COUNTER_IN)
+               ret = add_iptables_in(&counter);
+       else if (counter.iotype == NFACCT_COUNTER_OUT)
+               ret = add_iptables_out(&counter);
+       else {
+               _E("Unknown counter direction: %s", counter.name);
+               return FALSE;
+       }
+
+       if (ret != RESOURCED_ERROR_NONE)
+               return FALSE;
+
+       if (found_counter != NULL && found_counter->state ==
+               NFACCT_STATE_DEACTIVATED)
+               found_counter->state = NFACCT_STATE_ACTIVE;
+       else
+               keep_counter(&counter);
+
+       return FALSE;
+}
+
+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;
+       struct iftype_context ctx;
+       m_data = get_shared_modules_data();
+       ret_msg_if(m_data == NULL,
+               "Can't get module data!");
+       iftype = get_iftype(ifindex);
+
+       ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
+               "Can't get iftype for remove counter");
+
+       ctx.iftype = iftype;
+       ctx.carg = m_data->carg;
+       g_tree_foreach(ctx.carg->nf_cntrs, activate_each_counter_by_iftype, &ctx);
+       add_tizen_os_counters(m_data->carg);
+}
+
+struct del_counter_context
+{
+       struct nfacct_value *nfacct_value;
+       struct nfacct_key *nfacct_key;
+       struct counter_arg *carg;
+};
+
+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 = del_ctx->nfacct_value;
+       struct nfacct_key *nfacct_key = del_ctx->nfacct_key;
+
+       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 = del_counter(&counter);
+
+       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ECORE_CALLBACK_CANCEL,
+               "Can't delete counter %s",
+               counter.name);
+
+       nfacct_value->state = NFACCT_STATE_DEACTIVATED;
+
+       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 */
+       if (ctx->iftype != nfacct_key->iftype)
+               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;
+       ecore_timer_add(0, 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;
+       struct iftype_context ctx;
+       m_data = get_shared_modules_data();
+       ret_msg_if(m_data == NULL,
+               "Can't get module data!");
+       iftype = get_iftype(ifindex);
+
+       ret_msg_if(iftype == RESOURCED_IFACE_UNKNOWN,
+               "Can't get iftype for remove counter");
+
+       ctx.iftype = iftype;
+       ctx.carg = m_data->carg;
+       g_tree_foreach(ctx.carg->nf_cntrs, deactivate_each_counter_by_iftype, &ctx);
+}
+
+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;
+       char pkgname[MAX_PATH_LENGTH];
+       extract_pkgname(app_id, pkgname, sizeof(pkgname));
+       ret = make_net_cls_cgroup_with_pid(pid, pkgname);
+       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)
diff --git a/src/network/datausage-quota-processing.c b/src/network/datausage-quota-processing.c
new file mode 100644 (file)
index 0000000..8018032
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ * 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 "database.h"
+#include "data_usage.h"
+#include "macro.h"
+#include "protocol-info.h"
+#include "resourced.h"
+#include "notification.h"
+#include "storage.h"
+#include "trace.h"
+#include "roaming.h"
+#include "datausage-restriction.h"
+#include "datausage-vconf-common.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 "\
+       "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) "\
+       "GROUP BY qt.binpath, qt.iftype, qt.sent_quota, qt.rcv_quota, " \
+       "qt.roaming";
+
+static const char insert_query[] = "REPLACE INTO effective_quotas " \
+       "(binpath, sent_used_quota, rcv_used_quota, " \
+       "start_time, finish_time, iftype, roaming, state) " \
+       " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+
+static const char clear_effective_quota_query[] = "DELETE FROM effective_quotas " \
+       " WHERE binpath = ? AND iftype = ? AND roaming = ?";
+
+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;
+};
+
+typedef enum {
+       DROP_UNDEF = 0,
+       DROP_NEED = 1,
+       DROP_NO_NEED = 2
+} drop_decision;
+
+static void obtain_and_keep_quotas(sqlite3_stmt *query)
+{
+       int rc = 0;
+       struct quota *value = 0;
+       struct quota_key *key = 0;
+
+       if (!query) {
+               _D("Can not update quotas: empty query");
+               return;
+       }
+
+       do {
+               rc = sqlite3_step(query);
+
+               if (rc == SQLITE_ERROR) {
+                       _E("Error updating quotas %s", sqlite3_errmsg(resourced_get_database()));
+                       return;
+               } else if (rc == SQLITE_ROW) {
+                       value = g_new0(struct quota, 1);
+                       if (!value) {
+                               _E("Can't allocate value for quota");
+                               return;
+                       }
+
+                       key = g_new0(struct quota_key, 1);
+                       if (!key) {
+                               _E("Can't allocate key for quota");
+                               goto free_value;
+                       }
+
+                       key->app_id = strdup((char *)sqlite3_column_text(
+                               query, 0));
+                       key->iftype = sqlite3_column_int(
+                               query, 11);
+                       key->roaming = sqlite3_column_int(
+                               query, 12);
+
+                       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);
+
+                       g_tree_insert(quotas, key, value);
+               }
+       } while (rc == SQLITE_ROW);
+
+       return;
+free_value:
+       if (value)
+               g_free(value);
+}
+
+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;
+       /* the first part of the key is equal compare second */
+       return strcmp(key1->app_id, key2->app_id) ||
+               key1->iftype - key2->iftype ||
+               key1->roaming - key2->roaming;
+}
+
+#define quota_key_destructor g_free
+#define quota_destructor g_free
+
+static void _clear_effective_quota(const char *app_id,
+       const resourced_iface_type iftype,
+       const resourced_roaming_type roaming)
+{
+       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_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;
+};
+
+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;
+
+       if (!context ||
+           (context->roaming != RESOURCED_ROAMING_UNKNOWN &&
+            context->roaming != info->roaming))
+               return RESOURCED_CONTINUE;
+
+       context->sent_used_quota = info->foreground.cnt.incoming_bytes;
+       context->rcv_used_quota = info->foreground.cnt.outgoing_bytes;
+       return RESOURCED_CANCEL; /* only one entry allowed */
+}
+
+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_step(insert_stmt) != SQLITE_DONE)
+               _D("Failed to record quotas %s", sqlite3_errmsg(resourced_get_database()));
+       sqlite3_reset(insert_stmt);
+}
+
+static void _set_effective_quota(const char *app_id,
+       const resourced_iface_type iftype, const time_t start_time,
+       const int time_period,
+       const resourced_roaming_type roaming)
+{
+       data_usage_selection_rule rule = {0,};
+       struct data_usage_context out_context = {0,};
+       struct quota_key key_quota = {
+               .app_id = app_id,
+               .iftype = iftype,
+               .roaming = roaming,
+       };
+       struct quota app_quota = {0,};
+       const time_t cur_time = time(0);
+       char buf[28];
+
+       if (cur_time < start_time) {
+               _D("No need to update effective quota!");
+               return;
+       }
+
+       out_context.roaming = roaming;
+       rule.from = start_time;
+       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;
+       }
+
+       if (ctime_r(&start_time, buf) == NULL) {
+               _E("Input parameter start_time is invalid");
+               return;
+       }
+
+       _SD("Get counted traffic for appid:%s, per"
+               "%s, incoming:%d, outgoing:%d", app_id, buf,
+               out_context.rcv_used_quota, out_context.sent_used_quota);
+
+       app_quota.sent_used_quota = out_context.sent_used_quota;
+       app_quota.rcv_used_quota = out_context.rcv_used_quota;
+       app_quota.real_start = start_time;
+       app_quota.real_finish = _get_finish_time(start_time, time_period);
+       app_quota.state = RESOURCED_QUOTA_APPLIED;
+       _record_quota(&key_quota, &app_quota);
+}
+
+void update_quota_state(const char *app_id,
+                       const resourced_iface_type iftype,
+                       const time_t start_time,
+                       const int time_period,
+                       const resourced_roaming_type roaming)
+{
+       struct quota_key key;
+       struct quota *tree_value;
+
+       if (!app_id) {
+               _SE("app_id must be not NULL");
+               return;
+       }
+
+       key.app_id = app_id;
+       key.iftype = iftype;
+       key.roaming = roaming;
+       tree_value = (struct quota *)g_tree_search(quotas,
+           (GCompareFunc)compare_quota_key, &key);
+
+       if (tree_value && tree_value->state == RESOURCED_QUOTA_APPLIED) {
+               _SD("Removing quota and restriction for %s,%d", app_id, iftype);
+               /* Restrictions can't be separated */
+               remove_restriction_local(app_id, iftype);
+               g_tree_remove(quotas, (gconstpointer*)(&key));
+               _clear_effective_quota(app_id, iftype, roaming);
+
+               if (start_time && time_period)
+                       _set_effective_quota(app_id, iftype, start_time,
+                       time_period, roaming);
+       } else
+               _SD("There is no quota %s,%d in tree", app_id, iftype);
+}
+
+static resourced_ret_c _init_quotas(void)
+{
+       execute_once {
+               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 _update_quotas(void)
+{
+       const resourced_ret_c ret = _init_quotas();
+       if (ret != RESOURCED_ERROR_NONE) {
+               _E("Failed to init quotas");
+               return ret;
+       }
+
+       obtain_and_keep_quotas(select_stmt);
+       return RESOURCED_ERROR_NONE;
+}
+
+static const int64_t quota_gap_value[RESOURCED_IFACE_ALL] = {
+       5000,   /* ~4.5MB UNKNOWN */
+       5000,   /* ~3MB RESOURCED_IFACE_DATACALL */
+       6000000,        /* ~6MB RESOURCED_IFACE_WIFI */
+       5000000,        /* ~100MB RESOURCED_IFACE_WIRED */
+       6000000,        /* ~6MB RESOURCED_IFACE_BLUETOOTH */
+};
+
+static const int64_t quota_datacall_gap_value[RESOURCED_PROTOCOL_MAX_ELEM] = {
+       5000,  /* RESOURCED_PROTOCOL_NONE */
+       5000,  /* RESOURCED_PROTOCOL_DATACALL_NOSVC */
+       5000,  /* RESOURCED_PROTOCOL_DATACALL_EMERGENCY */
+       5000,  /* RESOURCED_PROTOCOL_DATACALL_SEARCH */
+       5000,  /* RESOURCED_PROTOCOL_DATACALL_2G */
+       5000,  /* RESOURCED_PROTOCOL_DATACALL_2_5G #GPRS 40 kbit/s in practice */
+       18750, /* RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE 150 kbit/s in practice */
+       400000, /* RESOURCED_PROTOCOL_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_hw_net_protocol_type(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];
+}
+
+int _is_under_restriction(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 void _check_warning_threshold(const int64_t send_delta, const int64_t rcv_delta,
+       struct quota *app_quota, const char *appid)
+{
+       ret_msg_if(!app_quota, "Please provide valid pointer");
+
+       if (send_delta <= app_quota->snd_warning_threshold ||
+           rcv_delta <= app_quota->rcv_warning_threshold) {
+               app_quota->snd_warning_threshold = 0;
+               app_quota->rcv_warning_threshold = 0;
+               send_restriction_warn_notification(appid);
+       }
+}
+
+inline static int _get_warning_limit(int64_t limit, int threshold)
+{
+       if (limit < threshold) {
+               _E("Warning threshold is greater than limit!");
+               return WARNING_THRESHOLD_DEFAULT; /* 0 means kernel will
+                                               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 gboolean check_and_apply_node(gpointer key,
+                                    gpointer value, gpointer user_data)
+{
+       struct quota *app_quota = value;
+       struct quota_key *key_quota = key;
+       int64_t send_delta, rcv_delta;
+       struct daemon_opts *opts = (struct daemon_opts *)user_data;
+       resourced_net_restrictions rst = { RESOURCED_STATE_UNKNOWN,
+                                          RESOURCED_IFACE_UNKNOWN };
+
+       /* do not check already applied quota*/
+       if (app_quota->state == RESOURCED_QUOTA_APPLIED)
+               return FALSE;
+
+       send_delta = app_quota->send_quota - app_quota->sent_used_quota;
+       rcv_delta = app_quota->rcv_quota - app_quota->rcv_used_quota;
+
+       if (app_quota->send_quota <= 0 || app_quota->rcv_quota <= 0)
+               send_restriction_notification(key_quota->app_id);
+       else
+               _check_warning_threshold(send_delta, rcv_delta, app_quota,
+                       key_quota->app_id);
+
+       if (_is_under_restriction(send_delta, rcv_delta, key_quota->iftype,
+               opts->update_period) &&
+           (key_quota->roaming == RESOURCED_ROAMING_UNKNOWN ||
+            key_quota->roaming == get_roaming())) {
+               if (!strcmp(key_quota->app_id, TETHERING_APP_NAME) &&
+                   (send_delta > 0 || rcv_delta > 0))
+                       /* in the case of tethering we send
+                          restriction only that must apply now */
+                       return FALSE;
+
+               rst.send_limit = cast_restriction_limit(send_delta);
+               rst.rcv_limit = cast_restriction_limit(rcv_delta);
+               rst.snd_warning_limit = _get_warning_limit(
+                       rst.send_limit, app_quota->snd_warning_threshold);
+               rst.rcv_warning_limit = _get_warning_limit(
+                       rst.rcv_limit, app_quota->rcv_warning_threshold);
+
+               _SD("Applying quota for %s, iftype %d", key_quota->app_id,
+                   key_quota->iftype);
+               rst.iftype = key_quota->iftype;
+
+               if (proc_keep_restriction(key_quota->app_id,
+                                             app_quota->quota_id, &rst,
+                                             RST_SET) == RESOURCED_ERROR_NONE) {
+                       app_quota->state = RESOURCED_QUOTA_APPLIED;
+                       _D("Restriction was applied successfully.");
+               }
+       }
+
+       return FALSE; /* continue iteration */
+}
+
+static void check_and_apply_quota(volatile struct daemon_opts *opts)
+{
+       g_tree_foreach(quotas, check_and_apply_node, (void *)opts);
+}
+
+struct update_all_arg
+{
+       resourced_iface_type iftype;
+       struct application_stat *app_stat;
+};
+
+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;
+
+       /* 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) &&
+            (qkey->roaming == RESOURCED_ROAMING_UNKNOWN ||
+             qkey->roaming == arg->app_stat->is_roaming)) ||
+           !strcmp(qkey->app_id, TETHERING_APP_NAME)) {
+               struct quota *total_quota = (struct quota *)value;
+               /* update it */
+               total_quota->sent_used_quota += arg->app_stat->delta_snd;
+               total_quota->rcv_used_quota += arg->app_stat->delta_rcv;
+               arg->app_stat->delta_snd = 0;
+               arg->app_stat->delta_rcv = 0;
+               _D("update total_quota tx:%"PRId64";rx:%"PRId64" iftype %d ifindex %d\n",
+                  total_quota->sent_used_quota, total_quota->rcv_used_quota,
+                       arg->iftype, arg->app_stat->ifindex);
+
+       }
+
+       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;
+       _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);
+       *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;
+
+       /* 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;
+       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)
+{
+       _D("reset_quota called");
+       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;
+
+       _SD("Removing restriction of quota for %s,%d", qkey->app_id,
+           qkey->iftype);
+       if (remove_restriction_local(qkey->app_id, qkey->iftype)
+           == 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 application_stat_tree *apps,
+       volatile struct daemon_opts *opts)
+{
+       /* For first initialization */
+       static int quota_updated;
+
+       if (opts && opts->is_update_quota) {
+               const int error = _update_quotas();
+               if (error)
+                       return error;
+               quota_updated = 1;
+       }
+
+       actualize_quota_table(apps);
+
+       check_and_apply_quota(opts);
+
+       /* finilize state */
+       if (opts && opts->is_update_quota && quota_updated) {
+               opts->is_update_quota = 0;
+               quota_updated = 0;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+/**
+ * Release  statement
+ */
+void finalize_quotas(void)
+{
+       finalize_statement(&insert_stmt);
+       finalize_statement(&select_stmt);
+       finalize_statement(&clear_effective_stmt);
+       g_tree_destroy(quotas);
+}
+
diff --git a/src/network/datausage-quota.c b/src/network/datausage-quota.c
new file mode 100644 (file)
index 0000000..7983177
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * 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(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[10];
+       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);
+       return send_quota_message(RESOURCED_NETWORK_CREATE_QUOTA, "sdttdddddd",
+               params);
+}
+
+static resourced_ret_c send_remove_quota_message(const char *app_id,
+       const resourced_iface_type iftype,
+       const resourced_roaming_type roaming_type)
+{
+       char *params[3];
+
+       serialize_params(params, ARRAY_SIZE(params), app_id, iftype,
+               roaming_type);
+       return send_quota_message(RESOURCED_NETWORK_REMOVE_QUOTA, "sdd",
+               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);
+}
+
+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("===============================");
+
+       return send_create_quota_message(app_id, &quota_to_send);
+}
diff --git a/src/network/datausage-vconf-callbacks.c b/src/network/datausage-vconf-callbacks.c
new file mode 100644 (file)
index 0000000..ae9098a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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 "daemon-options.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 <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)
+{
+       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;
+}
+
+static void datausage_timer_change_cb(keynode_t *key, void *data)
+{
+       struct daemon_opts *options = (struct daemon_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;
+}
+
+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);
+}
+
+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);
+}
diff --git a/src/network/datausage-vconf-common.c b/src/network/datausage-vconf-common.c
new file mode 100644 (file)
index 0000000..3d914b1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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;
+}
diff --git a/src/network/dummy-tethering.c b/src/network/dummy-tethering.c
new file mode 100644 (file)
index 0000000..9c18243
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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)
+{
+}
+
diff --git a/src/network/dummy_roaming.c b/src/network/dummy_roaming.c
new file mode 100644 (file)
index 0000000..c67fee6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  resourced
+ *
+ * Copyright (c) 2013 - 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 roaming.c
+ *
+ * @desc It's dummy implementation for none telephony case.
+ */
+
+#include "macro.h"
+#include "roaming.h"
+#include "trace.h"
+
+/* for avoiding dependency in this file */
+
+void regist_roaming_cb(roaming_cb UNUSED cb)
+{
+       _D("ROAMING ISN'T SUPPORTED, CHECK TELEPHONY MODULE");
+}
+
+resourced_roaming_type get_roaming(void)
+{
+       _D("ROAMING ISN'T SUPPORTED, CHECK TELEPHONY MODULE");
+       return RESOURCED_ROAMING_UNKNOWN;
+}
+
diff --git a/src/network/foreach.c b/src/network/foreach.c
new file mode 100644 (file)
index 0000000..0afb353
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * 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 from statistics where time_stamp between ? and ? " \
+       "group by binpath, is_roaming order by received desc"
+
+#define DATA_USAGE_FOR_PERIOD_IFACE "select binpath, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, "                       \
+       "sum(sent) as sent from statistics where time_stamp between ? and ? " \
+       "and iftype=? group by binpath, is_roaming order by received desc"
+
+#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 " \
+       "from statistics where time_stamp between ? and ? " \
+       "group by binpath, time_stamp order by time_stamp"
+
+#define DATA_USAGE_CHUNKS_IFACE "select binpath, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, "                       \
+       "sum(sent) as sent, time_stamp as start_time, "                 \
+       "time_stamp - time_stamp % ? as time_stamp " \
+       "from statistics where time_stamp between ? and ? and iftype=?" \
+       "group by binpath, time_stamp order by time_stamp"
+
+#define DATA_USAGE_APP_DETAILS "select iftype, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname from statistics where time_stamp between ? and ? " \
+       "and binpath=? group by binpath, iftype, is_roaming order by iftype"
+
+#define DATA_USAGE_APP_DETAILS_IFACE "select iftype, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname from statistics where time_stamp between ? and ? " \
+       "and binpath=? and iftype=?"
+
+#define DATA_USAGE_CHUNKS_APP "select iftype, hw_net_protocol_type, "  \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname, time_stamp - time_stamp % ? as time_stamp " \
+       "from statistics where time_stamp between ? and ? and binpath = ? " \
+       "group by iftype, time_stamp order by time_stamp, iftype"
+
+#define DATA_USAGE_CHUNKS_APP_IFACE "select iftype, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname, time_stamp - time_stamp % ? as time_stamp " \
+       "from statistics where time_stamp between ? and ? and binpath = ? " \
+       "and iftype = ? group by time_stamp order by time_stamp"
+
+#define DATA_USAGE_TOTAL "select iftype, hw_net_protocol_type, "       \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname from statistics where time_stamp between ? and ? " \
+       "group by iftype order by iftype, is_roaming"
+
+#define DATA_USAGE_TOTAL_IFACE "select iftype, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname from statistics where time_stamp between ? and ? " \
+       "and iftype=?"
+
+#define DATA_USAGE_CHUNKS_TOTAL "select iftype, hw_net_protocol_type, "        \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname, time_stamp - time_stamp % ? as time_stamp " \
+       "from statistics where time_stamp between ? and ? "             \
+       "group by iftype, time_stamp order by time_stamp, iftype"
+
+#define DATA_USAGE_CHUNKS_TOTAL_IFACE "select iftype, hw_net_protocol_type, " \
+       "is_roaming, sum(received) as received, sum(sent) as sent, "    \
+       "ifname, time_stamp - time_stamp % ? as time_stamp " \
+       "from statistics where time_stamp between ? and ? "             \
+       "and iftype = ? group by time_stamp order by time_stamp"
+
+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;
+       resourced_counters *cnt = &data.foreground.cnt;
+       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);
+                       cnt->incoming_bytes = sqlite3_column_int64(stm, 3);
+                       cnt->outgoing_bytes = sqlite3_column_int64(stm, 4);
+                       if (rule->granularity) {
+                               interval.from = sqlite3_column_int64(stm, 5);
+                               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)
+{
+       return *details_stms[is_iftype_defined(rule->iftype) |
+       (app_id ? 0 : 2) | (rule->granularity ? 4 : 0)];
+}
+
+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;
+       resourced_counters *cnt = &data.foreground.cnt;
+       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);
+                       cnt->incoming_bytes = sqlite3_column_int64(stm, 3);
+                       cnt->outgoing_bytes = sqlite3_column_int64(stm, 4);
+                       data.ifname = (char *)sqlite3_column_text(stm, 5);
+
+                       if (rule->granularity) {
+                               interval.from = sqlite3_column_int64(stm, 6);
+                               interval.to = interval.from + rule->granularity;
+                       }
+                       data.app_id = (char *)sqlite3_column_text(stm, 0);
+
+
+                       if (info_cb(&data, user_data) == RESOURCED_CANCEL)
+                               rc = SQLITE_DONE; /* emulate end of data */
+                       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;
+}
diff --git a/src/network/generic-netlink.c b/src/network/generic-netlink.c
new file mode 100644 (file)
index 0000000..a7f0e8e
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+ * 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 */
+
+/**
+ * @desc accepts opaque pointer
+ * extracts command id
+ */
+inline int netlink_get_command(struct genl *nl_ans)
+{
+       return nl_ans->g.cmd;
+}
+
+uint32_t netlink_get_family(struct genl *nl_ans)
+{
+       return nl_ans->n.nlmsg_type;
+}
+
+static void fill_traf_stat_list(char *buffer, __u16 count,
+                       traffic_stat_tree *stats)
+{
+       struct traffic_event *cur = (struct traffic_event *)buffer;
+
+       while (count) {
+               struct traffic_stat *to_insert;
+               struct classid_iftype_key *key;
+
+               to_insert = g_new(struct traffic_stat, 1);
+               if (!to_insert) {
+                       _D("Can't allocate %d bytes for traffic_stat\n", sizeof(struct traffic_stat));
+                       return;
+               }
+
+               key = g_new(struct classid_iftype_key, 1);
+
+               if (!key) {
+                       _D("Can't allocate %d bytes for classid_iftype_key\n", sizeof(struct classid_iftype_key));
+                       g_free((gpointer)to_insert);
+                       return;
+               }
+
+               to_insert->bytes = cur->bytes;
+               to_insert->ifindex = cur->ifindex;
+               key->classid = cur->sk_classid ?
+                       cur->sk_classid : RSML_UNKNOWN_CLASSID;
+               key->iftype = get_iftype(cur->ifindex);
+               g_tree_insert((GTree *) stats, (gpointer)key, to_insert);
+               --count;
+               ++cur;
+       }
+}
+
+/*
+ * Send netlink message to kernel
+ */
+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 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;
+}
+
+static void _process_answer(struct netlink_serialization_params *params)
+{
+       struct genl *nl_ans = params->ans;
+       traffic_stat_tree *stats = params->stat_tree;
+       ssize_t remains;
+       char *buffer;
+       struct nlattr *first_na, *second_na;
+       int first_len;
+       int count = 0;
+
+       remains = GENLMSG_PAYLOAD(&nl_ans->n);
+       if (remains <= 0)
+               return;
+
+       /* parse reply message */
+       first_na = (struct nlattr *)GENLMSG_DATA(nl_ans);
+
+       /* inline nla_next() */
+       first_len = NLA_ALIGN(first_na->nla_len);
+
+       second_na = (struct nlattr *) ((char *) first_na + first_len);
+       remains -= first_len;
+
+       /* but we need data_attr->nla_len */
+       buffer = (char *) malloc((size_t)remains);
+       if (buffer == NULL)
+               return;
+
+       if (first_na->nla_type == TRAF_STAT_COUNT) {
+               count = *(__u16 *) NLA_DATA(first_na);
+               memcpy(buffer, (char *) NLA_DATA(second_na),
+                      second_na->nla_len);
+       } else {
+               _D("Expected attribute %d got %d", TRAF_STAT_COUNT, first_na->nla_type);
+       }
+
+       if (count > 0)
+               fill_traf_stat_list(buffer, count, stats);
+       free(buffer);
+
+}
+
+netlink_serialization_command *netlink_create_command(
+       struct netlink_serialization_params *params)
+{
+       static netlink_serialization_command command = {0,};
+       const int netlink_command = netlink_get_command(params->ans);
+
+       command.params = *params;
+
+       if (netlink_command == TRAF_STAT_C_GET_CONN_IN) {
+               command.deserialize_answer = _process_answer;
+               command.params.stat_tree = params->carg->in_tree;
+       } else if (netlink_command == TRAF_STAT_C_GET_PID_OUT) {
+               command.deserialize_answer = _process_answer;
+               command.params.stat_tree = params->carg->out_tree;
+       } else {
+               _E("Unknown command!");
+               return NULL;
+       }
+
+       return &command;
+}
+
+resourced_ret_c process_netlink_restriction_msg(const struct genl *ans,
+       struct traffic_restriction *restriction, uint8_t *command)
+{
+       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;
+}
diff --git a/src/network/iface-cb.c b/src/network/iface-cb.c
new file mode 100644 (file)
index 0000000..6f2457f
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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;
+}
+
+static void apply_iface_options(void)
+{
+       resourced_options options = { 0 };
+
+       load_options(&options);
+       set_wifi_allowance(options.wifi);
+       set_datacall_allowance(options.datacall);
+}
+
+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");
+       apply_iface_options();
+       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);
+       finalize_iftypes();
+       g_list_free_full(ifcallbacks, free);
+}
diff --git a/src/network/iface.c b/src/network/iface.c
new file mode 100644 (file)
index 0000000..3182a49
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * 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 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 "config.h"
+#include "config-parser.h"
+#include "const.h"
+#include "iface.h"
+#include "macro.h"
+#include "trace.h"
+
+#define NET_INTERFACE_NAMES_FILE "/etc/resourced/network.conf"
+#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 GTree *ifnames; /* for keeping ifype - interface name association */
+
+static pthread_rwlock_t iftypes_guard = PTHREAD_RWLOCK_INITIALIZER;
+static pthread_rwlock_t ifnames_guard = PTHREAD_RWLOCK_INITIALIZER;
+
+
+static 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];
+};
+
+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 put_ifname_to_tree(GTree *ifnames_tree, char *ifname, int iftype)
+{
+       int name_len = strlen(ifname) + 1;
+       gpointer new_value = (gpointer)malloc(name_len);
+       if (!new_value) {
+               _E("Malloc of put_ifname_to_tree failed\n");
+               return;
+       }
+       strncpy(new_value, ifname, name_len);
+
+       if (!ifnames_tree) {
+               free(new_value);
+               _E("Please provide valid argument!");
+               return;
+       }
+       g_tree_replace(ifnames_tree, (gpointer)iftype, new_value);
+}
+
+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 (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;
+}
+
+static 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, ret;
+       char buf[256];
+       resourced_iface_type iftype;
+       struct if_nameindex *ids = if_nameindex();
+       GTree *iftypes_next = create_iface_tree();
+       GTree *ifnames_next = create_iface_tree();
+
+       if (ids == NULL) {
+               strerror_r(errno, buf, sizeof(buf));
+               _E("Failed to initialize iftype table! errno: %d, %s",
+                       errno, buf);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if (!ifnames_relations) {
+               ret = config_parse(NET_INTERFACE_NAMES_FILE,
+                                  fill_ifaces_relation, NULL);
+               if (ret != 0)
+                       _D("Can't parse config file %s",
+                          NET_INTERFACE_NAMES_FILE);
+       }
+
+       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);
+               put_iftype_to_tree(iftypes_next, ids[i].if_index, iftype);
+               /*  we know here iftype/ids[i].if_name, lets populate
+                *      ifnames_tree */
+               put_ifname_to_tree(ifnames_next, ids[i].if_name, iftype);
+               _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);
+       reset_tree(ifnames_next, &ifnames, &ifnames_guard);
+       return RESOURCED_ERROR_NONE;
+}
+
+void finalize_iftypes(void)
+{
+       reset_tree(NULL, &iftypes, &iftypes_guard);
+       reset_tree(NULL, &ifnames, &ifnames_guard);
+       g_slist_free_full(ifnames_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_allowed_ifindex(int ifindex)
+{
+       return iface_stat[get_iftype(ifindex)];
+}
+
+resourced_iface_type get_iftype(int ifindex)
+{
+       return get_iftype_from_tree(iftypes, ifindex);
+}
+
+static gboolean print_ifname(gpointer key, gpointer value, gpointer data)
+{
+       _D("ifname %s", (char *)value);
+       return FALSE;
+}
+
+static char *get_ifname_from_tree(GTree *ifnames_tree, int iftype)
+{
+       char *ret = NULL;
+
+       ret_value_msg_if(!ifnames_tree, NULL, "Please provide valid argument!");
+
+       pthread_rwlock_rdlock(&ifnames_guard);
+       ret = (char *)g_tree_lookup(ifnames_tree, (gpointer)iftype);
+       pthread_rwlock_unlock(&ifnames_guard);
+       if (ret == NULL)
+               g_tree_foreach(ifnames_tree, print_ifname, NULL);
+
+       return ret;
+}
+
+char *get_iftype_name(resourced_iface_type iftype)
+{
+       return get_ifname_from_tree(ifnames, iftype);
+}
+
+static gboolean search_loopback(gpointer key,
+                  gpointer value,
+                  gpointer data)
+{
+       int *res = (int *)data;
+       if (!value)
+               return FALSE;
+       *res = *(int *)value == RESOURCED_IFACE_UNKNOWN ? TRUE : FALSE;
+       return *res;
+}
+
+static bool is_only_loopback(GTree *iftypes_tree)
+{
+       int nodes = g_tree_nnodes(iftypes_tree);
+       int res = 0;
+
+       if (nodes > 1)
+               return false;
+
+       g_tree_foreach(iftypes_tree, search_loopback, &res);
+       return res;
+}
+
+void for_each_ifindex(ifindex_iterator iter, void(*empty_func)(void *),
+       void *data)
+{
+       pthread_rwlock_rdlock(&iftypes_guard);
+       if (!is_only_loopback(iftypes))
+               g_tree_foreach(iftypes, (GTraverseFunc)iter, data);
+       else if (empty_func)
+               empty_func(data);
+
+       pthread_rwlock_unlock(&iftypes_guard);
+}
+
+void set_wifi_allowance(const resourced_option_state wifi_option)
+{
+       iface_stat[RESOURCED_IFACE_WIFI] = wifi_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
+}
+
+void set_datacall_allowance(const resourced_option_state datacall_option)
+{
+       iface_stat[RESOURCED_IFACE_DATACALL] = datacall_option == RESOURCED_OPTION_ENABLE ? 1 : 0;
+}
diff --git a/src/network/include/counter-process.h b/src/network/include/counter-process.h
new file mode 100644 (file)
index 0000000..8ef555e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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 */
diff --git a/src/network/include/counter.h b/src/network/include/counter.h
new file mode 100644 (file)
index 0000000..3e89fdc
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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 "daemon-options.h"
+
+#include <Ecore.h>
+
+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
+       struct daemon_opts *opts;
+       struct application_stat_tree *result;
+       traffic_stat_tree *in_tree;
+       traffic_stat_tree *out_tree;
+       Ecore_Timer *ecore_timer;
+       Ecore_Fd_Handler *ecore_fd_handler;
+       Ecore_Timer *store_result_timer;
+};
+
+/**
+ * @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 daemon_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_ */
+
+
diff --git a/src/network/include/database.h b/src/network/include/database.h
new file mode 100644 (file)
index 0000000..1390d35
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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_ */
diff --git a/src/network/include/datausage-common.h b/src/network/include/datausage-common.h
new file mode 100644 (file)
index 0000000..49b8cf9
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 "iface.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);
+
+#endif /* __RESOURCED_NETSTAT_COMMON_H__ */
diff --git a/src/network/include/datausage-foreach.h b/src/network/include/datausage-foreach.h
new file mode 100644 (file)
index 0000000..aa4c9f4
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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_ */
+
diff --git a/src/network/include/datausage-quota-processing.h b/src/network/include/datausage-quota-processing.h
new file mode 100644 (file)
index 0000000..016520a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 "app-stat.h"
+#include "resourced.h"
+
+/*
+ * Store data in effective quota
+ */
+void flush_quota_table(void);
+
+/*
+ * Quota processing. It's apply quota if needed.
+ * And actualize current quotas state.
+ */
+resourced_ret_c process_quota(struct application_stat_tree *apps,
+       volatile struct daemon_opts *opts);
+
+/*
+ * Finish working with quotas
+ */
+void finalize_quotas(void);
+
+/*
+ * Delete quota and drop remove restriction
+ */
+void update_quota_state(const char *app_id, const resourced_iface_type iftype,
+       const time_t start_time, const int time_period,
+       const resourced_roaming_type roaming);
+
+#endif /* _TRESOURCED_DATAUSAGE_QUOTA_PROCESSING_H_ */
diff --git a/src/network/include/datausage-quota.h b/src/network/include/datausage-quota.h
new file mode 100644 (file)
index 0000000..d20cb41
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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_ */
diff --git a/src/network/include/datausage-reset.h b/src/network/include/datausage-reset.h
new file mode 100644 (file)
index 0000000..9393238
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_ */
diff --git a/src/network/include/datausage-restriction.h b/src/network/include/datausage-restriction.h
new file mode 100644 (file)
index 0000000..e1ee46a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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 "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);
+
+/**
+ * @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);
+
+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);
+
+resourced_ret_c remove_restriction_local(const char *app_id,
+                                        const resourced_iface_type iftype);
+
+resourced_ret_c exclude_restriction_local(const char *app_id,
+                                         const int quota_id,
+                                         const resourced_iface_type iftype);
+
+#endif /* _RESOURCED_RESTRICTION_H_ */
diff --git a/src/network/include/datausage-vconf-callbacks.h b/src/network/include/datausage-vconf-callbacks.h
new file mode 100644 (file)
index 0000000..2208c0e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 */
diff --git a/src/network/include/datausage-vconf-common.h b/src/network/include/datausage-vconf-common.h
new file mode 100644 (file)
index 0000000..5d23d76
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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);
diff --git a/src/network/include/errors.h b/src/network/include/errors.h
new file mode 100644 (file)
index 0000000..59c256a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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_ */
diff --git a/src/network/include/generic-netlink.h b/src/network/include/generic-netlink.h
new file mode 100644 (file)
index 0000000..975db67
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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_*/
diff --git a/src/network/include/iface-cb.h b/src/network/include/iface-cb.h
new file mode 100644 (file)
index 0000000..3889e53
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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_ */
diff --git a/src/network/include/iface.h b/src/network/include/iface.h
new file mode 100644 (file)
index 0000000..3e70d0e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.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 "data_usage.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;
+
+int init_iftype(void);
+void finalize_iftypes(void);
+
+int is_allowed_ifindex(int ifindex);
+
+resourced_iface_type get_iftype(int ifindex);
+char *get_iftype_name(resourced_iface_type iftype);
+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);
+
+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 GList iface_callbacks;
+
+#endif /*TRESOURCED_LIBS_NET_IFACE_H_*/
diff --git a/src/network/include/net-cls-cgroup.h b/src/network/include/net-cls-cgroup.h
new file mode 100644 (file)
index 0000000..70eec15
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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"
+
+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 take classid from net_cls cgroup with name pkg_name
+ * @param pkg_name - name of the cgroup
+ * @param create - in case of true - create cgroup if it's not exists
+ * @return classid
+ */
+u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create);
+
+/**
+ * @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);
+
+#endif /*_RESOURCED_NET_CLS_CGROUP_H_*/
diff --git a/src/network/include/netlink-restriction.h b/src/network/include/netlink-restriction.h
new file mode 100644 (file)
index 0000000..1df4f03
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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
+ * 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 resourced_iface_type iftype,
+                        const int send_limit, const int rcv_limit,
+                        const int snd_warning_threshold,
+                        const int rcv_warning_threshold);
+
+#endif /* RESOURCED_NET_RESTRICTION_H_ */
diff --git a/src/network/include/nfacct-rule.h b/src/network/include/nfacct-rule.h
new file mode 100644 (file)
index 0000000..16b00e1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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>
+
+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_RULE_LAST_ELEM,
+} nfacct_rule_intend;
+
+struct nfacct_rule {
+       char name[MAX_NAME_LENGTH];
+       char ifname[MAX_NAME_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;
+};
+
+struct counter_arg;
+
+void generate_counter_name(struct nfacct_rule *counter);
+bool recreate_counter_by_name(char *cnt_name, struct nfacct_rule *counter);
+
+resourced_ret_c  nfacct_send_get(struct counter_arg *carg);
+resourced_ret_c nfacct_send_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 */
+
diff --git a/src/network/include/nl-helper.h b/src/network/include/nl-helper.h
new file mode 100644 (file)
index 0000000..0ef417b
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 {
+       traffic_stat_tree *stat_tree;
+       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_ */
diff --git a/src/network/include/notification.h b/src/network/include/notification.h
new file mode 100644 (file)
index 0000000..fb28122
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 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
+
+void send_restriction_notification(const char *appid);
+void send_restriction_warn_notification(const char *appid);
+
+#endif /* _RESOURCED_DATAUSAGE_NOTIFICATION_H */
+
diff --git a/src/network/include/protocol-info.h b/src/network/include/protocol-info.h
new file mode 100644 (file)
index 0000000..3a8d4ac
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * @file protocol-info.h
+ *
+ * @desc Network protocol entity: now it's only for
+ * datacall network interface type
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#ifndef RESOURCED_PROTOCOL_INFO_NET_IFACE_H_
+#define RESOURCED_PROTOCOL_INFO_NET_IFACE_H_
+
+#include "data_usage.h"
+
+void init_hw_net_protocol_type(void);
+
+void finalize_hw_net_protocol_type(void);
+
+resourced_hw_net_protocol_type get_hw_net_protocol_type(
+       const resourced_iface_type iftype);
+
+#endif /* RESOURCED_PROTOCOL_INFO_NET_IFACE_H_ */
diff --git a/src/network/include/restriction-handler.h b/src/network/include/restriction-handler.h
new file mode 100644 (file)
index 0000000..28a7297
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 "roaming.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);
+
+/**
+ * @brief This function returns pointer to roaming
+ * callback. No need to free memory.
+ */
+roaming_cb get_roaming_restriction_cb(void);
+
+void reactivate_restrictions(void);
+
+typedef GList list_restrictions_info;
+
+#endif /* _RESOURCED_RESTRICTION_HANDLER_H_ */
diff --git a/src/network/include/restriction-helper.h b/src/network/include/restriction-helper.h
new file mode 100644 (file)
index 0000000..0ba219f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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_ */
diff --git a/src/network/include/roaming.h b/src/network/include/roaming.h
new file mode 100644 (file)
index 0000000..d527fd9
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 roaming.h
+ *
+ * @desc Roaming persistent object. Due roaming changes not so often we can keep it in
+ *  our memory and handle roaming changes.
+ */
+
+#ifndef _RSML_LIBS_ROAMING_H
+#define _RSML_LIBS_ROAMING_H
+
+#include "data_usage.h"
+
+/**
+ * @brief Just get roaming state.
+ */
+resourced_roaming_type get_roaming(void);
+
+typedef void(*roaming_cb)(void);
+
+void regist_roaming_cb(roaming_cb cb);
+
+#endif /* _RSML_LIBS_ROAMING_H*/
diff --git a/src/network/include/settings.h b/src/network/include/settings.h
new file mode 100644 (file)
index 0000000..303c831
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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_SETTINGS_H_
+#define TRESOURCED_LIBS_SETTINGS_H_
+
+#include "data_usage.h"
+
+#define RESOURCED_WIFI_STATISTICS_PATH "db/private/resourced/wifi_statistics"
+#define RESOURCED_DATACALL_PATH "db/private/resourced/datacall"
+#define RESOURCED_DATAUSAGE_TIMER_PATH "db/private/resourced/datausage_timer"
+#define RESOURCED_DATACALL_LOGGING_PATH "db/private/resourced/datacall_logging"
+
+int load_options(resourced_options *options);
+
+#endif /*TRESOURCED_LIBS_SETTINGS_H_*/
diff --git a/src/network/include/specific-trace.h b/src/network/include/specific-trace.h
new file mode 100644 (file)
index 0000000..43588b3
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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__*/
diff --git a/src/network/include/storage.h b/src/network/include/storage.h
new file mode 100644 (file)
index 0000000..97fc9b3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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
+ * @param flush_period - Time interval for storing data
+ * @return 1 if flushed, 0 if not
+ */
+int store_result(struct application_stat_tree *stats, int flush_period);
+
+/**
+ * @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_*/
diff --git a/src/network/include/tethering-restriction.h b/src/network/include/tethering-restriction.h
new file mode 100644 (file)
index 0000000..047cc05
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 "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_ */
diff --git a/src/network/join-dummy.c b/src/network/join-dummy.c
new file mode 100644 (file)
index 0000000..cc816c7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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;
+}
diff --git a/src/network/join.c b/src/network/join.c
new file mode 100644 (file)
index 0000000..83c39da
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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"
+
+#ifdef NETWORK_SERVICE_OFF
+API resourced_ret_c join_app_performance(const char *app_id, const pid_t pid)
+{
+       return RESOURCED_ERROR_NONE;
+}
+
+#else
+
+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(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);
+}
+#endif
diff --git a/src/network/ktgrabber-restriction.c b/src/network/ktgrabber-restriction.c
new file mode 100644 (file)
index 0000000..aad8618
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * 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 resourced_iface_type iftype,
+                        const int send_limit, const int rcv_limit,
+                        const int snd_warning_threshold,
+                        const int rcv_warning_threshold)
+{
+       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;
+}
diff --git a/src/network/main.c b/src/network/main.c
new file mode 100644 (file)
index 0000000..9b056e4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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");
+
+       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;
+       }
+
+       /* 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)
+{
+       finalize_datausage_reset();
+       finalize_datausage_foreach();
+       finalize_datausage_restriction();
+       sqlite3_close(database);
+}
+
+API sqlite3 *resourced_get_database(void)
+{
+       libresourced_db_initialize_once();
+       return database;
+}
diff --git a/src/network/net-activity.c b/src/network/net-activity.c
new file mode 100644 (file)
index 0000000..be1cacb
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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;
+}
diff --git a/src/network/net-cls-cgroup.c b/src/network/net-cls-cgroup.c
new file mode 100644 (file)
index 0000000..4764cfa
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * 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 "appid-helper.h"
+#include "net-cls-cgroup.h"
+#include "cgroup.h"
+#include "const.h"
+#include "data_usage.h"
+#include "errors.h"
+#include "file-helper.h"
+#include "macro.h"
+#include "trace.h"
+
+#include <dirent.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define CUR_CLASSID_PATH "/tmp/cur_classid"
+#define CLASSID_FILE_NAME "/net_cls.classid"
+#define PATH_TO_NET_CGROUP_DIR "/sys/fs/cgroup/net_cls"
+
+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_int(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 generated_classid = produce_classid();
+       if (classid)
+               *classid = generated_classid;
+
+       snprintf(buf, sizeof(buf), "%s/%s", cgroup, subdir);
+       return cgroup_write_node(buf, CLASSID_FILE_NAME, generated_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)
+{
+       char pkgname[MAX_PATH_LENGTH];
+       extract_pkgname(app_id, pkgname, sizeof(pkgname));
+       return get_classid_by_pkg_name(pkgname, create);
+}
+
+API u_int32_t get_classid_by_pkg_name(const char *pkg_name, int create)
+{
+       int ret = 0;
+       int exists;
+       u_int32_t classid = RESOURCED_UNKNOWN_CLASSID;
+
+       if (!strcmp(pkg_name, RESOURCED_ALL_APP))
+               return RESOURCED_ALL_APP_CLASSID;
+
+       if (!strcmp(pkg_name, TETHERING_APP_NAME))
+               return RESOURCED_TETHERING_APP_CLASSID;
+
+       /* just read */
+       if (!create)
+               classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR,
+                       pkg_name);
+
+       if (classid != RESOURCED_UNKNOWN_CLASSID)
+               return classid;
+
+       ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name,
+               &exists);
+       if (ret)
+               goto handle_error;
+
+       if (exists)
+               classid = get_classid_from_cgroup(PATH_TO_NET_CGROUP_DIR,
+                       pkg_name);
+       else
+               ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR,
+                       (char *)pkg_name, &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);
+
+       if (ret)
+               return RESOURCED_ERROR_FAIL;
+
+       _D("class id table updated");
+       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 strdup(TETHERING_APP_NAME);
+       array_foreach(tc, struct task_classid, tasks_classids)
+               if (classid == tc->classid)
+                       return strdup(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;
+
+       _D("can't resolve app id");
+       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_with_pid(const int pid, const char *pkg_name)
+{
+       int ret = 0;
+       int exists = 0;
+
+       if (pkg_name == NULL) {
+               _E("package name must be not empty");
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+
+       _SD("pkg: %s; pid: %d\n", pkg_name, pid);
+
+       ret = make_cgroup_subdir(PATH_TO_NET_CGROUP_DIR, (char *)pkg_name, &exists);
+       ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
+
+       if (!exists) {
+               ret = place_classid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name,
+                       NULL);
+               ret_value_if(ret < 0, RESOURCED_ERROR_FAIL);
+       }
+
+       return place_pid_to_cgroup(PATH_TO_NET_CGROUP_DIR, pkg_name, pid);
+}
+
diff --git a/src/network/network-dummy.c b/src/network/network-dummy.c
new file mode 100644 (file)
index 0000000..e6f0380
--- /dev/null
@@ -0,0 +1,150 @@
+ /*
+ * resourced
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * @file network.c
+ *
+ * @desc Entity for storing applications statistics
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+
+#include "data_usage.h"
+#include "datausage-restriction.h"
+#include "macro.h"
+#include "net-cls-cgroup.h"
+#include "rd-network.h"
+#include "resourced.h"
+
+API network_error_e network_set_option(const network_option_s *options)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_get_option(network_option_s *options)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_make_cgroup_with_pid(const int pid,
+       const char *pkg_name)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_set_restriction(const char *app_id,
+                           const network_restriction_s *restriction)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_restriction_foreach(network_restriction_cb restriction_cb,
+                               void *user_data)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_remove_restriction(const char *app_id)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_remove_restriction_by_iftype(const char *app_id,
+                                            const network_iface_e iftype)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_exclude_restriction(const char *app_id)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_exclude_restriction_by_iftype(
+       const char *app_id, const network_iface_e iftype)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_register_activity_cb(network_activity_cb activity_cb)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_join_app_performance(const char *app_id, const pid_t pid)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_update_statistics(void)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_foreach(const network_selection_rule_s *rule,
+                            network_info_cb info_cb, void *user_data)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_details_foreach(const char *app_id,
+                                          network_selection_rule_s *rule,
+                                          network_info_cb info_cb,
+                                          void *user_data)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_reset(const network_reset_rule_s *rule)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_remove_quota(
+       const network_quota_reset_rule_s *rule)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_remove_quota_by_iftype(
+       const char *app_id, const network_iface_e iftype)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_set_quota(const char *app_id,
+                             const network_quota_s *quota)
+{
+       return NETWORK_ERROR_NONE;
+}
+
+API network_error_e network_get_restriction_state(const char *pkg_id,
+       network_iface_e iftype, network_restriction_state *state)
+{
+       *state = NETWORK_RESTRICTION_UNDEFINDED;
+       return NETWORK_ERROR_NONE;
+}
diff --git a/src/network/network.c b/src/network/network.c
new file mode 100644 (file)
index 0000000..225e059
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * @file network.c
+ *
+ * @desc Entity for storing applications statistics
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <stdio.h>
+
+#include "data_usage.h"
+#include "datausage-restriction.h"
+#include "macro.h"
+#include "net-cls-cgroup.h"
+#include "rd-network.h"
+#include "resourced.h"
+
+API network_error_e network_set_option(const network_option_s *options)
+{
+       return (network_error_e)set_resourced_options((const resourced_options*)options);
+}
+
+API network_error_e network_get_option(network_option_s *options)
+{
+       return (network_error_e)get_resourced_options((resourced_options*)options);
+}
+
+API network_error_e network_make_cgroup_with_pid(const int pid,
+       const char *pkg_name)
+{
+       return (network_error_e)make_net_cls_cgroup_with_pid(pid, pkg_name);
+}
+
+API u_int32_t network_get_classid_by_pkg_name(const char *pkg_name, int create)
+{
+       return (network_error_e)get_classid_by_pkg_name(pkg_name, create);
+}
+
+API network_error_e network_set_restriction(const char *app_id,
+                           const network_restriction_s *restriction)
+{
+       return (network_error_e)set_net_restriction(app_id,
+                       (const resourced_net_restrictions*)restriction);
+}
+
+API network_error_e network_restriction_foreach(network_restriction_cb restriction_cb,
+                               void *user_data)
+{
+       return (network_error_e)restrictions_foreach((resourced_restriction_cb)restriction_cb, user_data);
+}
+
+API network_error_e network_remove_restriction(const char *app_id)
+{
+       return (network_error_e)remove_restriction(app_id);
+}
+
+API network_error_e network_remove_restriction_by_iftype(const char *app_id,
+                                            const network_iface_e iftype)
+{
+       return (network_error_e)remove_restriction_by_iftype(app_id, (const resourced_iface_type)iftype);
+}
+
+API network_error_e network_exclude_restriction(const char *app_id)
+{
+       return (network_error_e)exclude_restriction(app_id);
+}
+
+API network_error_e network_exclude_restriction_by_iftype(
+       const char *app_id, const network_iface_e iftype)
+{
+       return (network_error_e)exclude_restriction_by_iftype(
+               app_id, (const resourced_iface_type)iftype);
+}
+
+API network_error_e network_register_activity_cb(network_activity_cb activity_cb)
+{
+       return (network_error_e)register_net_activity_cb((net_activity_cb)activity_cb);
+}
+
+API network_error_e network_join_app_performance(const char *app_id, const pid_t pid)
+{
+       return (network_error_e)join_app_performance(app_id, pid);
+}
+
+API network_error_e network_update_statistics(void)
+{
+       return (network_error_e)resourced_update_statistics();
+}
+
+API network_error_e network_foreach(const network_selection_rule_s *rule,
+                            network_info_cb info_cb, void *user_data)
+{
+       return (network_error_e)data_usage_foreach(
+                       (const data_usage_selection_rule*)rule,
+                       (data_usage_info_cb)info_cb,
+                       user_data);
+}
+
+API network_error_e network_details_foreach(const char *app_id,
+                                          network_selection_rule_s *rule,
+                                          network_info_cb info_cb,
+                                          void *user_data)
+{
+       return (network_error_e)data_usage_details_foreach(app_id,
+                       (data_usage_selection_rule*)rule,
+                       (data_usage_info_cb)info_cb,
+                       user_data);
+}
+
+API network_error_e network_reset(const network_reset_rule_s *rule)
+{
+       return (network_error_e)reset_data_usage((const data_usage_reset_rule*)rule);
+}
+
+API network_error_e network_remove_quota(
+       const network_quota_reset_rule_s *rule)
+{
+       return (network_error_e)remove_datausage_quota(
+                       (const struct datausage_quota_reset_rule*)rule);
+}
+
+API network_error_e network_remove_quota_by_iftype(
+       const char *app_id, const network_iface_e iftype)
+{
+       return (network_error_e)remove_datausage_quota_by_iftype(app_id,
+                       (const resourced_iface_type)iftype);
+}
+
+API network_error_e network_set_quota(const char *app_id,
+                             const network_quota_s *quota)
+{
+       return (network_error_e)set_datausage_quota(app_id,
+                       (const data_usage_quota*)quota);
+}
+
+API network_error_e network_get_restriction_state(const char *pkg_id,
+       network_iface_e iftype, network_restriction_state *state)
+{
+       return (network_error_e)get_restriction_state(pkg_id,
+               (const resourced_iface_type)iftype,
+               (resourced_restriction_state *)state);
+}
diff --git a/src/network/network.conf b/src/network/network.conf
new file mode 100644 (file)
index 0000000..d48100f
--- /dev/null
@@ -0,0 +1,6 @@
+[IFACES_TYPE]
+datacall=rmnet
+datacall=pdp
+datacall=seth_w
+
+
diff --git a/src/network/nf-restriction.c b/src/network/nf-restriction.c
new file mode 100644 (file)
index 0000000..e2704e9
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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 "trace.h"
+
+static resourced_ret_c apply_net_restriction(struct nfacct_rule *rule,
+                       const int send_limit, const int rcv_limit)
+{
+       nfacct_rule_jump jump = rule->intend == NFACCT_WARN ? NFACCT_JUMP_ACCEPT :
+                       NFACCT_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_ACCEPT,
+               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 resourced_iface_type iftype,
+                        const int send_limit, const int rcv_limit,
+                        const int snd_warning_threshold,
+                        const int rcv_warning_threshold)
+{
+       int ret;
+       struct shared_modules_data *m_data = get_shared_modules_data();
+       struct counter_arg *carg;
+       struct nfacct_rule rule = {
+               .name = {0},
+               .ifname = {0},
+               0,
+       };
+
+       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;
+
+       if (rst_type == RST_SET) {
+               if (snd_warning_threshold ||
+                       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;
+}
diff --git a/src/network/nfacct-rule.c b/src/network/nfacct-rule.c
new file mode 100644 (file)
index 0000000..ad8f45d
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+ * 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 "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"
+
+/* TODO idea to use the same rule both for BLOCK (REJECT) and WARNING (ACCEPT) */
+#define RULE_APP_OUT "%s -w %s OUTPUT -o %s -m cgroup --cgroup %u %s %s"
+#define RULE_APP_IN "%s -w %s INPUT -i %s -m cgroup --cgroup %u %s %s"
+
+#define RULE_IFACE_OUT "%s -w %s OUTPUT -o %s %s -m cgroup ! --cgroup 0 %s"
+#define RULE_IFACE_IN "%s -w %s INPUT -i %s %s -m cgroup ! --cgroup 0 %s"
+
+#define NFNL_SUBSYS_ACCT                7
+
+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 */
+};
+
+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);
+       _D("counter name %s", counter->name);
+       /* padding */
+       add_uint64_attr(&req, 0, NFACCT_PKTS);
+       add_uint64_attr(&req, 0, NFACCT_BYTES);
+       if (counter->quota) {
+               _D("quota bytes %"PRId64, counter->quota);
+               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);
+}
+
+static resourced_ret_c nfacct_send_del(struct nfacct_rule *counter)
+{
+       struct genl req;
+
+       prepare_netlink_msg(&req, NFNL_MSG_ACCT_DEL, NLM_F_ACK);
+       add_string_attr(&req, counter->name, NFACCT_NAME);
+       return send_nfacct_request(counter->carg->sock, &req);
+}
+#define NFACCT_F_QUOTAS (NFACCT_F_QUOTA_BYTES | NFACCT_F_QUOTA_PKTS)
+resourced_ret_c nfacct_send_get(struct counter_arg *carg)
+{
+       struct genl req;
+       struct nlattr *na;
+       prepare_netlink_msg(&req, NFNL_MSG_ACCT_GET_CTRZERO,
+               NLM_F_DUMP);
+       /* due we don't get counter with quota any where else,
+       * here we will request just counters by default */
+       na = start_nest_attr(&req, NFACCT_FILTER);
+       add_uint32_attr(&req, NFACCT_F_QUOTAS,
+               NFACCT_FILTER_ATTR_MASK);
+       add_uint32_attr(&req, 0, NFACCT_FILTER_ATTR_VALUE);
+       end_nest_attr(&req, na);
+       return send_nfacct_request(carg->sock, &req);
+}
+
+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;
+
+       switch (cnt_name[0]) {
+       case 'c':
+               cnt->intend  = NFACCT_COUNTER;
+               break;
+       case 'w':
+               cnt->intend  = NFACCT_WARN;
+               break;
+       case 'r':
+               cnt->intend  = NFACCT_BLOCK;
+               break;
+       default:
+               return false;
+       }
+
+       io_part = strtok_r(cnt_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(struct nfacct_rule *rule, pid_t pid)
+{
+       int status;
+       char buf[256];
+
+       pid_t ret_pid = waitpid(pid, &status, 0);
+       if (ret_pid < 0) {
+               strerror_r(errno, buf, sizeof(buf));
+               _D("can't wait for a pid %d %d %s", pid, status, 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);
+       _D("check rule %s", exec_buf);
+       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];
+
+               _D("executing iptables cmd %s in forked process", cmd_buf);
+               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);
+               for (i = 1; i <= args_number; ++i) {
+                       args[i] = strtok_r(NULL, " ", &saveptr);
+               }
+               args[i] = NULL;
+
+               ret = execv(cmd, args);
+               if (ret) {
+                       strerror_r(errno, buf, sizeof(buf));
+                       _E("Can't execute %s: %s",
+                               cmd_buf, 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 *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,
+               iftype_name, nfacct, jump);
+       ret_value_msg_if(ret > sizeof(block_buf), RESOURCED_ERROR_NONE,
+               "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_NONE,
+               "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_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_new(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set nfacct counter");
+               }
+
+               /* 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 = 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_APPEND) {
+                       /* TODO here and everywhere should be not just a del,
+                        *      here should be get counted value and than
+                        *      set new counter with that value, but it's minor issue,
+                        *      due it's not clear when actual counters was stored,
+                        *      and based on which value settings made such decition */
+                       wait_for_rule_cmd(rule, pid);
+                       ret = nfacct_send_del(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set nfacct counter");
+               }
+       }
+
+       if (iotype & NFACCT_COUNTER_OUT) {
+               /* outcome part */
+               rule->iotype = NFACCT_COUNTER_OUT;
+               rule->quota = send_limit;
+               generate_counter_name(rule);
+               if (action != NFACCT_ACTION_DELETE) {
+                       ret = nfacct_send_new(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set quota counter");
+               }
+
+               ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                       "can't set counter");
+
+               ret = snprintf(nfacct_buf, sizeof(nfacct_buf), NFACCT_NAME_MOD,
+                       rule->name);
+               ret = exec_app_cmd(RULE_APP_OUT, set_cmd, nfacct_buf,
+                       jump_cmd, rule->classid, choose_iftype_name(rule), &pid);
+               ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
+                       RESOURCED_ERROR_FAIL, "Can't set conditional block for engress"
+                               " traffic, for classid %u, cmd %s, j %s",
+                               rule->classid, set_cmd, jump_cmd);
+               if (action != NFACCT_ACTION_APPEND) {
+                       wait_for_rule_cmd(rule, pid);
+                       ret = nfacct_send_del(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't del nfacct counter");
+               }
+       }
+       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;
+
+       if (iotype & NFACCT_COUNTER_IN) {
+               /* income part */
+               rule->quota = rcv_limit;
+               rule->iotype = NFACCT_COUNTER_IN;
+               generate_counter_name(rule);
+
+               if (action != NFACCT_ACTION_DELETE) {
+                       ret = nfacct_send_new(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set quota counter");
+               }
+
+               ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
+                       NFACCT_NAME_MOD, rule->name);
+               ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+                               "Not enought buffer");
+
+               ret = exec_iface_cmd(RULE_IFACE_IN, set_cmd, nfacct_buf,
+                       jump_cmd, choose_iftype_name(rule), &pid);
+               ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
+                       RESOURCED_ERROR_NONE, "Can't set conditional block for ingress"
+                               " traffic, for iftype %d, cmd %s, j %s",
+                               rule->iftype, set_cmd, jump_cmd);
+
+               if (action != NFACCT_ACTION_APPEND) {
+                       wait_for_rule_cmd(rule, pid);
+                       ret = nfacct_send_del(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set quota counter");
+               }
+       }
+
+       if (iotype & NFACCT_COUNTER_OUT) {
+               /* outcome part */
+               rule->iotype = NFACCT_COUNTER_OUT;
+               rule->quota = send_limit;
+               generate_counter_name(rule);
+
+               if (action != NFACCT_ACTION_DELETE) {
+                       ret = nfacct_send_new(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set quota counter");
+               }
+
+               ret = snprintf(nfacct_buf, sizeof(nfacct_buf),
+                               NFACCT_NAME_MOD, rule->name);
+               ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+                               "Not enough buffer");
+
+               ret = exec_iface_cmd(RULE_IFACE_OUT, set_cmd, nfacct_buf,
+                       jump_cmd, choose_iftype_name(rule), &pid);
+               ret_value_msg_if(ret != RESOURCED_ERROR_NONE,
+                       RESOURCED_ERROR_FAIL, "Can't set conditional block for "
+                               " engress traffic, for iftype %d, cmd %s, j %s",
+                               rule->iftype, set_cmd, jump_cmd);
+
+               if (action != NFACCT_ACTION_APPEND) {
+                       wait_for_rule_cmd(rule, pid);
+                       ret = nfacct_send_del(rule);
+                       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+                               "can't set quota counter");
+               }
+       }
+       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';
+       char *iftype_name = get_iftype_name(counter->iftype);
+       ret_msg_if(iftype_name == NULL, "Can't get interface name!");
+       STRING_SAVE_COPY(counter->ifname, iftype_name);
+
+       if (counter->intend  == NFACCT_WARN)
+               warn_symbol = 'w';
+       else if (counter->intend  == NFACCT_BLOCK)
+               warn_symbol = 'r';
+       if (counter->classid != RESOURCED_ALL_APP_CLASSID)
+               snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%d_%s",
+                       warn_symbol, counter->iotype, counter->iftype,
+                       counter->classid, counter->ifname);
+       else
+               snprintf(counter->name, MAX_NAME_LENGTH, "%c%d_%d_%s",
+                       warn_symbol, counter->iotype, counter->iftype,
+                       counter->ifname);
+
+       }
+
diff --git a/src/network/nl-helper.c b/src/network/nl-helper.c
new file mode 100644 (file)
index 0000000..4e2e13f
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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
diff --git a/src/network/notification.c b/src/network/notification.c
new file mode 100644 (file)
index 0000000..f8f96b1
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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 notification.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 "roaming.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"
+
+static int show_restriction_popup(const char *value)
+{
+       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(&quota_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_WATCHDOG, SYSTEM_POPUP_IFACE_WATCHDOG, 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)
+{
+       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);
+}
+
+void send_restriction_warn_notification(const char *appid)
+{
+       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);
+}
+
diff --git a/src/network/options.c b/src/network/options.c
new file mode 100644 (file)
index 0000000..f0927e7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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)
+{
+       return save_options(options) ? RESOURCED_ERROR_FAIL : RESOURCED_ERROR_NONE;
+}
+
+API resourced_ret_c get_resourced_options(resourced_options *options)
+{
+       return load_options(options) ? RESOURCED_ERROR_FAIL : RESOURCED_ERROR_NONE;
+}
diff --git a/src/network/protocol-info.c b/src/network/protocol-info.c
new file mode 100644 (file)
index 0000000..e166717
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * @file protocol-info.c
+ *
+ * @desc Network protocol entity: now it's only for
+ * datacall network interface type
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+#include <vconf/vconf.h>
+
+#include "macro.h"
+#include "protocol-info.h"
+#include "resourced.h"
+#include "trace.h"
+
+static resourced_hw_net_protocol_type datacall_prot_t =        RESOURCED_PROTOCOL_NONE;
+
+static resourced_hw_net_protocol_type _convert_to_resourced_protocol(
+       const int prot_type)
+{
+       switch (prot_type) {
+       case VCONFKEY_TELEPHONY_SVCTYPE_NOSVC:
+               return RESOURCED_PROTOCOL_DATACALL_NOSVC;
+       case VCONFKEY_TELEPHONY_SVCTYPE_EMERGENCY:
+               return RESOURCED_PROTOCOL_DATACALL_EMERGENCY;
+       case VCONFKEY_TELEPHONY_SVCTYPE_SEARCH:
+               return RESOURCED_PROTOCOL_DATACALL_SEARCH;
+       case VCONFKEY_TELEPHONY_SVCTYPE_2G:
+               return RESOURCED_PROTOCOL_DATACALL_2G;
+       case VCONFKEY_TELEPHONY_SVCTYPE_2_5G:
+               return RESOURCED_PROTOCOL_DATACALL_2_5G;
+       case VCONFKEY_TELEPHONY_SVCTYPE_2_5G_EDGE:
+               return RESOURCED_PROTOCOL_DATACALL_2_5G_EDGE;
+       case VCONFKEY_TELEPHONY_SVCTYPE_3G:
+               return RESOURCED_PROTOCOL_DATACALL_3G;
+       case VCONFKEY_TELEPHONY_SVCTYPE_HSDPA:
+               return RESOURCED_PROTOCOL_DATACALL_HSDPA;
+       case VCONFKEY_TELEPHONY_SVCTYPE_LTE:
+               return RESOURCED_PROTOCOL_DATACALL_LTE;
+       case VCONFKEY_TELEPHONY_SVCTYPE_NONE:
+       default:
+               return RESOURCED_PROTOCOL_NONE;
+       }
+}
+
+static resourced_ret_c _get_protocol_type(
+       resourced_hw_net_protocol_type *prot_type)
+{
+       int ret, status;
+
+       ret = vconf_get_int(VCONFKEY_TELEPHONY_SVCTYPE, &status);
+       ret_value_msg_if(ret != 0, RESOURCED_ERROR_FAIL,
+                        "vconf get failed(VCONFKEY_TELEPHONY_SVCTYPE)\n");
+       *prot_type = _convert_to_resourced_protocol(status);
+       return RESOURCED_ERROR_NONE;
+}
+
+static void _datacall_protocol_type_change_cb(keynode_t *key, void *data)
+{
+       int val = vconf_keynode_get_int(key);
+
+       _D("key = %s, value = %d(int)\n", vconf_keynode_get_name(key), val);
+       datacall_prot_t = _convert_to_resourced_protocol(val);
+}
+
+void init_hw_net_protocol_type(void)
+{
+       vconf_notify_key_changed(VCONFKEY_TELEPHONY_SVCTYPE,
+                                _datacall_protocol_type_change_cb, NULL);
+       if (_get_protocol_type(&datacall_prot_t) != RESOURCED_ERROR_NONE)
+               _E("_get_protocol_type failed\n");
+}
+
+void finalize_hw_net_protocol_type(void)
+{
+       vconf_ignore_key_changed(VCONFKEY_TELEPHONY_SVCTYPE,
+                                _datacall_protocol_type_change_cb);
+       datacall_prot_t = RESOURCED_PROTOCOL_NONE;
+}
+
+resourced_hw_net_protocol_type get_hw_net_protocol_type(
+       const resourced_iface_type iftype)
+{
+       if (iftype == RESOURCED_IFACE_DATACALL)
+               return datacall_prot_t;
+
+       return RESOURCED_PROTOCOL_NONE;
+}
diff --git a/src/network/reset.c b/src/network/reset.c
new file mode 100644 (file)
index 0000000..766a1a0
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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 ?"
+
+/* the following array is strictly ordered
+ * to find required statement the following code will be used:
+ * (app ? 1 : 0) | (iftype ? 2 : 0)
+ */
+static sqlite3_stmt *reset_stms[4];
+
+#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);
+
+       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(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;
+}
+
diff --git a/src/network/restriction-handler.c b/src/network/restriction-handler.c
new file mode 100644 (file)
index 0000000..efa4af0
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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 "const.h"
+#include "iface.h"
+#include "macro.h"
+#include "net-cls-cgroup.h"
+#include "trace.h"
+#include "restriction-helper.h"
+#include "datausage-restriction.h"
+#include "restriction-handler.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 = strdup(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;
+       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);
+       gpointer data = _create_reset_restriction(info, context->ifindex);
+       if (data)
+               context->restrictions = g_list_prepend(context->restrictions,
+                       data);
+       return RESOURCED_CONTINUE;
+}
+
+enum restriction_apply_type
+{
+       KEEP_AS_IS,
+       UNSET,
+};
+
+struct apply_param
+{
+       enum restriction_apply_type apply_type;
+};
+
+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)
+               rst_type = convert_to_restriction_type(arg->rst_state);
+       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);
+
+       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, &param);
+}
+
+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, &param);
+}
+
+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);
+       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);
+}
+
+static resourced_cb_ret roaming_restrictions_iter(
+       const resourced_restriction_info *info, void *user_data)
+{
+       struct apply_param param = {.apply_type = KEEP_AS_IS};
+       _reset_restrictions_iter((gpointer)info, &param);
+       return RESOURCED_CONTINUE;
+}
+
+static void handle_roaming_change(void)
+{
+       restrictions_foreach(roaming_restrictions_iter, NULL);
+}
+
+roaming_cb get_roaming_restriction_cb(void)
+{
+       return handle_roaming_change;
+}
+
+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();
+
+       strerror_r(errno, buf, sizeof(buf));
+       ret_msg_if(ids == NULL,
+                        "Failed to initialize iftype table! errno: %d, %s",
+                        errno, 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);
+}
diff --git a/src/network/restriction-helper.c b/src/network/restriction-helper.c
new file mode 100644 (file)
index 0000000..609b8c0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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)
+{
+       /* We need to put RESOURCED_IFACE_ALL type into the database,
+          in case of the "tethering" because it with no iftype */
+       return (app_classid == RESOURCED_TETHERING_APP_CLASSID) ?
+               RESOURCED_IFACE_ALL : 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);
+       return RESOURCED_ERROR_NONE;
+}
diff --git a/src/network/restriction-local.c b/src/network/restriction-local.c
new file mode 100644 (file)
index 0000000..6356300
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * 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 "roaming.h"
+#include "storage.h"
+#include "trace.h"
+#include "tethering-restriction.h"
+
+#define SET_NET_RESTRICTIONS "REPLACE INTO restrictions "     \
+       "(binpath, rcv_limit, send_limit, iftype, rst_state, "\
+       " quota_id, roaming) " \
+       "VALUES (?, ?, ?, ?, ?, ?, ?)"
+
+#define GET_NET_RESTRICTION "SELECT rcv_limit, send_limit, " \
+       " rst_state, quota_id FROM restrictions " \
+       "WHERE binpath = ? AND iftype = ?"
+
+#define RESET_RESTRICTIONS "DELETE FROM restrictions " \
+       "WHERE binpath=? AND iftype=?"
+
+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)
+{
+       resourced_ret_c error_code = init_reset_rst();
+
+       ret_value_if(error_code != RESOURCED_ERROR_NONE, error_code);
+
+       DB_ACTION(sqlite3_bind_text(reset_rst_stm, 1, app_id, -1, SQLITE_TRANSIENT));
+       DB_ACTION(sqlite3_bind_int(reset_rst_stm, 2, iftype));
+
+       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)
+{
+       resourced_ret_c error_code = RESOURCED_ERROR_NONE;
+
+       if (rst_state == RESOURCED_RESTRICTION_REMOVED)
+               return reset_restriction_db(app_id, iftype);
+
+       error_code = init_update_rest_stmt();
+       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));
+
+       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;
+}
+
+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;
+       int quota_id = 0;
+       sqlite3_stmt *stm = 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!");
+
+       _SD("%s, %d", app_id, iftype);
+
+       DB_ACTION(sqlite3_prepare_v2
+                 (resourced_get_database(), GET_NET_RESTRICTION, -1, &stm, NULL));
+
+       DB_ACTION(sqlite3_bind_text(stm, 1, app_id, -1, SQLITE_TRANSIENT));
+       DB_ACTION(sqlite3_bind_int(stm, 2, iftype));
+
+       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->quota_id = sqlite3_column_int(stm, 3);
+
+                       break;
+               case SQLITE_DONE:
+                       break;
+               case SQLITE_ERROR:
+               default:
+                       error_code = RESOURCED_ERROR_DB_FAILED;
+                       goto handle_error;
+               }
+       } while (rc == SQLITE_ROW);
+
+       _D("%d", quota_id);
+
+handle_error:
+
+       if (stm)
+               sqlite3_finalize(stm);
+
+       if (error_code == RESOURCED_ERROR_DB_FAILED)
+               _E("Failed to get network restriction's quota id: %s\n",
+                       sqlite3_errmsg(resourced_get_database()));
+
+       return quota_id;
+}
+
+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_roaming();
+       _D("roaming %d rst->roaming %d", roaming, rst->roaming);
+       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)
+{
+       int ret = RESOURCED_ERROR_NONE;
+
+       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)
+               ret = apply_tethering_restriction(rst_type);
+       if (classid != RESOURCED_TETHERING_APP_CLASSID &&
+           ret == RESOURCED_ERROR_NONE)
+               ret = send_net_restriction(rst_type, classid,
+                                                 rst->iftype,
+                                                 rst->send_limit,
+                                                 rst->rcv_limit,
+                                                 rst->snd_warning_limit,
+                                                 rst->rcv_warning_limit);
+       ret_value_msg_if(ret < 0, RESOURCED_ERROR_FAIL,
+                        "Restriction, type %d falied, return code %d\n",
+                        rst_type, ret);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+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)
+{
+       u_int32_t app_classid = 0;
+       resourced_iface_type store_iftype;
+       resourced_restriction_state rst_state;
+       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");
+
+       app_classid = get_classid_by_app_id(app_id, rst_type != RST_UNSET);
+       ret = process_kernel_restriction(app_classid, rst, rst_type);
+       ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+               "Can't keep restriction.");
+
+       store_iftype = get_store_iftype(app_classid, rst->iftype);
+       rst_state = convert_to_restriction_state(rst_type);
+       _SD("restriction: app_id %s, iftype %d, state %d, type %d\n", app_id,
+           store_iftype, rst_state, rst_type);
+
+       if (!strcmp(app_id, RESOURCED_ALL_APP) &&
+               rst->iftype == RESOURCED_IFACE_ALL)
+               process_net_block_state(rst_type);
+
+       return update_restriction_db(app_id, store_iftype,
+                                          rst->rcv_limit, rst->send_limit,
+                                          rst_state, quota_id, rst->roaming);
+}
+
+resourced_ret_c remove_restriction_local(const char *app_id,
+                                        const resourced_iface_type iftype)
+{
+       resourced_net_restrictions rst = { 0 };
+
+       rst.iftype = iftype;
+       return proc_keep_restriction(app_id, NONE_QUOTA_ID, &rst,
+                                        RST_UNSET);
+}
+
+resourced_ret_c exclude_restriction_local(const char *app_id,
+                                         const int quota_id,
+                                         const resourced_iface_type iftype)
+{
+       resourced_net_restrictions rst = { 0 };
+
+       rst.iftype = iftype;
+       return proc_keep_restriction(app_id, quota_id, &rst, RST_EXCLUDE);
+}
diff --git a/src/network/restriction.c b/src/network/restriction.c
new file mode 100644 (file)
index 0000000..ffd0ff3
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * 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 <rd-network.h>
+
+#include "cgroup.h"            /*get_classid_by_pkg_name */
+#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 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;
+}
+
+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[9];
+       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(BUS_NAME, RESOURCED_PATH_NETWORK,
+                                      RESOURCED_INTERFACE_NETWORK,
+                                      RESOURCED_NETWORK_PROCESS_RESTRICTION,
+                                      "sdddddddd", 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);
+
+                       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)
+{
+       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_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 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;
+       return process_restriction(app_id, &rst, RST_EXCLUDE);
+}
+
+API resourced_ret_c set_net_exclusion(const char *app_id,
+                       const resourced_net_restrictions *rst)
+{
+       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;
+}
+
diff --git a/src/network/roaming.c b/src/network/roaming.c
new file mode 100644 (file)
index 0000000..6152200
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *  resourced
+ *
+ * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ *
+ * @file roaming.c
+ *
+ * @desc Roaming persistent object. Due roaming changes not so often we can keep it in
+ *  our memory and handle roaming changes.
+ *  In this file we keep roaming state in global variable and change it in callback.
+ */
+
+#include <glib.h>
+#include <telephony_network.h>
+
+#include "roaming.h"
+#include "trace.h"
+#include "macro.h"
+
+static resourced_roaming_type roaming_state;
+
+/* for avoiding dependency in this file */
+
+static GSList *roaming_callbacks;
+
+static void invoke_roaming_callbacks(void)
+{
+       GSList *func_iter = NULL;
+       gslist_for_each_item(func_iter, roaming_callbacks) {
+               if (func_iter && func_iter->data)
+                       ((roaming_cb)func_iter->data)();
+       }
+}
+
+void regist_roaming_cb(roaming_cb cb)
+{
+       roaming_callbacks = g_slist_prepend(roaming_callbacks, cb);
+}
+
+static void on_roaming_change(bool new_roaming,
+       void UNUSED *user_data)
+{
+       _D("Roaming is changed %d", (int)new_roaming);
+       roaming_state = new_roaming ? RESOURCED_ROAMING_ENABLE : RESOURCED_ROAMING_DISABLE;
+       invoke_roaming_callbacks();
+}
+
+/**
+ * @brief Get initial value for roaming and sets callback for handling roaming change
+ */
+static void init_roaming_state(void)
+{
+       bool roaming = false;
+
+       if (network_info_set_roaming_state_changed_cb(on_roaming_change,
+               NULL) != NETWORK_INFO_ERROR_NONE)
+               _E("Can not register callback for handle roaming changes.");
+
+       if (network_info_is_roaming(&roaming) != NETWORK_INFO_ERROR_NONE)
+               _E("Failed to get initial roaming state!");
+
+       roaming_state = roaming ?
+               RESOURCED_ROAMING_ENABLE : RESOURCED_ROAMING_DISABLE;
+}
+
+resourced_roaming_type get_roaming(void)
+{
+       execute_once {
+               init_roaming_state();
+       }
+       return roaming_state;
+}
+
diff --git a/src/network/settings.c b/src/network/settings.c
new file mode 100644 (file)
index 0000000..d67e77f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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_options(resourced_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_int(RESOURCED_DATAUSAGE_TIMER_PATH, &val) == 0)
+               options->datausage_timer = val;
+       else {
+               _D("Can not get DataUsage timer 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;
+}
diff --git a/src/network/specific-trace.c b/src/network/specific-trace.c
new file mode 100644 (file)
index 0000000..f1d54d1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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, pid %d, rcv %u, snd %u, classid %u, iftype %d, ifname %s," \
+               " is_roaming %d",
+               appstat->application_id, appstat->pid, appstat->rcv_count,
+               appstat->snd_count, (u_int32_t)composite_key->classid,
+               composite_key->iftype, composite_key->ifname,
+               appstat->is_roaming);
+
+       return FALSE;
+}
diff --git a/src/network/storage.c b/src/network/storage.c
new file mode 100644 (file)
index 0000000..f0c67da
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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 "iface.h"
+#include "database.h"
+#include "datausage-quota-processing.h"
+#include "macro.h"
+#include "protocol-info.h"
+#include "storage.h"
+#include "specific-trace.h"
+#include "trace.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)
+{
+       _D("Handling network interface %d, %d", ifindex, state);
+
+       if (!update_iface_query) {
+               _E("Uninitialized statement");
+               return;
+       }
+       sqlite3_bind_int(update_iface_query, 1, get_iftype(ifindex));
+       sqlite3_bind_int(update_iface_query, 2, state);
+
+       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) " \
+                              "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_hw_net_protocol_type(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;
+       }
+
+       /*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;
+}
+
+int store_result(struct application_stat_tree *stats, int flush_period)
+{
+       time_t current_time;
+
+       time(&current_time);
+
+       if (current_time - stats->last_touch_time >= flush_period) {
+
+               pthread_rwlock_rdlock(&stats->guard);
+               WALK_TREE(stats->tree, print_appstat);
+               pthread_rwlock_unlock(&stats->guard);
+
+               if (init_update_statistics_query(resourced_get_database()) != SQLITE_OK) {
+                       _D("Failed to initialize data usage quota statements: %s\n",
+                          sqlite3_errmsg(resourced_get_database()));
+                       return 0; /* Do not iterate and free results */
+               }
+
+               /* it's reader only, we don't modify tree, don't reduce it,
+                *              due we want to reuse it in next iteration */
+               pthread_rwlock_rdlock(&stats->guard);
+               g_tree_foreach((GTree *) stats->tree,
+                              store_application_stat,
+                              &stats->last_touch_time);
+               pthread_rwlock_unlock(&stats->guard);
+               flush_quota_table();
+               time(&current_time);
+               stats->last_touch_time = current_time;
+               return 1;
+       }
+       return 0;
+}
+
+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;
+}
diff --git a/src/network/tethering-restriction.c b/src/network/tethering-restriction.c
new file mode 100644 (file)
index 0000000..ff84502
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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)
+{
+#ifdef TETHERING_FEATURE
+       static int tethering_exclude;
+       switch (type) {
+       case RST_SET:
+               if (!tethering_exclude)
+                       return fwrite_str(PATH_TO_PROC_IP_FORWARD, "0");
+               return RESOURCED_ERROR_NONE;
+       case RST_UNSET:
+               tethering_exclude = 0;
+               return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1");
+       case RST_EXCLUDE:
+               tethering_exclude = 1;
+               return fwrite_str(PATH_TO_PROC_IP_FORWARD, "1");
+       default:
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+
+       }
+#else
+       return RESOURCED_ERROR_NONE;
+#endif /* TETHERING_FEATURE */
+}
diff --git a/src/network/update.c b/src/network/update.c
new file mode 100755 (executable)
index 0000000..e141952
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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 "rd-network.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(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;
+}
+
diff --git a/src/powertop-wrapper/CMakeLists.txt b/src/powertop-wrapper/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7f9ae08
--- /dev/null
@@ -0,0 +1,33 @@
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+INCLUDE_DIRECTORIES(${INCLUDE_PUBLIC_DIR} ${POWERTOP-WRAPPER_SOURCE_DIR})
+
+FOREACH(flag ${pkg_${POWERTOP-WRAPPER}_CXXFLAGS})
+    SET(EXTRA_CXXFLAGS "${EXTRA_CXXFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXXFLAGS} -fPIC -Wall -Werror -pthread")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+
+IF("${ARCH}" STREQUAL "arm")
+    ADD_DEFINITIONS("-DTARGET")
+ENDIF("${ARCH}" STREQUAL "arm")
+
+ADD_DEFINITIONS("-DPREFIX=\"${CMAKE_INSTALL_PREFIX}\"")
+ADD_DEFINITIONS("-DSLP_DEBUG")
+
+SET(SOURCES ${POWERTOP-WRAPPER_SOURCE_DIR}/powertop-wrapper.cpp)
+ADD_LIBRARY(${POWERTOP-WRAPPER} SHARED ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${POWERTOP-WRAPPER} ${${POWERTOP_WRAPPER}_LDFLAGS})
+
+SET_TARGET_PROPERTIES(${POWERTOP-WRAPPER}
+     PROPERTIES
+     VERSION ${FULLVER}
+     SOVERSION ${MAJORVER}
+     CLEAN_DIRECT_OUTPUT 1
+)
+
+INSTALL(TARGETS ${POWERTOP-WRAPPER} DESTINATION lib)
+INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/powertop-dapi.h DESTINATION include/system)
diff --git a/src/powertop-wrapper/header.html b/src/powertop-wrapper/header.html
new file mode 100644 (file)
index 0000000..d293f69
--- /dev/null
@@ -0,0 +1,9795 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>PowerTOP report</title>
+<meta http-equiv="content-type" content="text/html;charset=utf-8">
+<script type="text/javascript">
+/*!
+ * jQuery JavaScript Library v1.8.0
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: Thu Aug 09 2012 16:24:48 GMT-0400 (Eastern Daylight Time)
+ */
+(function( window, undefined ) {
+var
+       // A central reference to the root jQuery(document)
+       rootjQuery,
+
+       // The deferred used on DOM ready
+       readyList,
+
+       // Use the correct document accordingly with window argument (sandbox)
+       document = window.document,
+       location = window.location,
+       navigator = window.navigator,
+
+       // Map over jQuery in case of overwrite
+       _jQuery = window.jQuery,
+
+       // Map over the $ in case of overwrite
+       _$ = window.$,
+
+       // Save a reference to some core methods
+       core_push = Array.prototype.push,
+       core_slice = Array.prototype.slice,
+       core_indexOf = Array.prototype.indexOf,
+       core_toString = Object.prototype.toString,
+       core_hasOwn = Object.prototype.hasOwnProperty,
+       core_trim = String.prototype.trim,
+
+       // Define a local copy of jQuery
+       jQuery = function( selector, context ) {
+               // The jQuery object is actually just the init constructor 'enhanced'
+               return new jQuery.fn.init( selector, context, rootjQuery );
+       },
+
+       // Used for matching numbers
+       core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
+
+       // Used for detecting and trimming whitespace
+       core_rnotwhite = /\S/,
+       core_rspace = /\s+/,
+
+       // IE doesn't match non-breaking spaces with \s
+       rtrim = core_rnotwhite.test("\xA0") ? (/^[\s\xA0]+|[\s\xA0]+$/g) : /^\s+|\s+$/g,
+
+       // A simple way to check for HTML strings
+       // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+       rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+       // Match a standalone tag
+       rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+       // JSON RegExp
+       rvalidchars = /^[\],:{}\s]*$/,
+       rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+       rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+       rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
+
+       // Matches dashed string for camelizing
+       rmsPrefix = /^-ms-/,
+       rdashAlpha = /-([\da-z])/gi,
+
+       // Used by jQuery.camelCase as callback to replace()
+       fcamelCase = function( all, letter ) {
+               return ( letter + "" ).toUpperCase();
+       },
+
+       // The ready event handler and self cleanup method
+       DOMContentLoaded = function() {
+               if ( document.addEventListener ) {
+                       document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+                       jQuery.ready();
+               } else if ( document.readyState === "complete" ) {
+                       // we're here because readyState === "complete" in oldIE
+                       // which is good enough for us to call the dom ready!
+                       document.detachEvent( "onreadystatechange", DOMContentLoaded );
+                       jQuery.ready();
+               }
+       },
+
+       // [[Class]] -> type pairs
+       class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+       constructor: jQuery,
+       init: function( selector, context, rootjQuery ) {
+               var match, elem, ret, doc;
+
+               // Handle $(""), $(null), $(undefined), $(false)
+               if ( !selector ) {
+                       return this;
+               }
+
+               // Handle $(DOMElement)
+               if ( selector.nodeType ) {
+                       this.context = this[0] = selector;
+                       this.length = 1;
+                       return this;
+               }
+
+               // Handle HTML strings
+               if ( typeof selector === "string" ) {
+                       if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+                               // Assume that strings that start and end with <> are HTML and skip the regex check
+                               match = [ null, selector, null ];
+
+                       } else {
+                               match = rquickExpr.exec( selector );
+                       }
+
+                       // Match html or make sure no context is specified for #id
+                       if ( match && (match[1] || !context) ) {
+
+                               // HANDLE: $(html) -> $(array)
+                               if ( match[1] ) {
+                                       context = context instanceof jQuery ? context[0] : context;
+                                       doc = ( context && context.nodeType ? context.ownerDocument || context : document );
+
+                                       // scripts is true for back-compat
+                                       selector = jQuery.parseHTML( match[1], doc, true );
+                                       if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+                                               this.attr.call( selector, context, true );
+                                       }
+
+                                       return jQuery.merge( this, selector );
+
+                               // HANDLE: $(#id)
+                               } else {
+                                       elem = document.getElementById( match[2] );
+
+                                       // Check parentNode to catch when Blackberry 4.6 returns
+                                       // nodes that are no longer in the document #6963
+                                       if ( elem && elem.parentNode ) {
+                                               // Handle the case where IE and Opera return items
+                                               // by name instead of ID
+                                               if ( elem.id !== match[2] ) {
+                                                       return rootjQuery.find( selector );
+                                               }
+
+                                               // Otherwise, we inject the element directly into the jQuery object
+                                               this.length = 1;
+                                               this[0] = elem;
+                                       }
+
+                                       this.context = document;
+                                       this.selector = selector;
+                                       return this;
+                               }
+
+                       // HANDLE: $(expr, $(...))
+                       } else if ( !context || context.jquery ) {
+                               return ( context || rootjQuery ).find( selector );
+
+                       // HANDLE: $(expr, context)
+                       // (which is just equivalent to: $(context).find(expr)
+                       } else {
+                               return this.constructor( context ).find( selector );
+                       }
+
+               // HANDLE: $(function)
+               // Shortcut for document ready
+               } else if ( jQuery.isFunction( selector ) ) {
+                       return rootjQuery.ready( selector );
+               }
+
+               if ( selector.selector !== undefined ) {
+                       this.selector = selector.selector;
+                       this.context = selector.context;
+               }
+
+               return jQuery.makeArray( selector, this );
+       },
+
+       // Start with an empty selector
+       selector: "",
+
+       // The current version of jQuery being used
+       jquery: "1.8.0",
+
+       // The default length of a jQuery object is 0
+       length: 0,
+
+       // The number of elements contained in the matched element set
+       size: function() {
+               return this.length;
+       },
+
+       toArray: function() {
+               return core_slice.call( this );
+       },
+
+       // Get the Nth element in the matched element set OR
+       // Get the whole matched element set as a clean array
+       get: function( num ) {
+               return num == null ?
+
+                       // Return a 'clean' array
+                       this.toArray() :
+
+                       // Return just the object
+                       ( num < 0 ? this[ this.length + num ] : this[ num ] );
+       },
+
+       // Take an array of elements and push it onto the stack
+       // (returning the new matched element set)
+       pushStack: function( elems, name, selector ) {
+
+               // Build a new jQuery matched element set
+               var ret = jQuery.merge( this.constructor(), elems );
+
+               // Add the old object onto the stack (as a reference)
+               ret.prevObject = this;
+
+               ret.context = this.context;
+
+               if ( name === "find" ) {
+                       ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+               } else if ( name ) {
+                       ret.selector = this.selector + "." + name + "(" + selector + ")";
+               }
+
+               // Return the newly-formed element set
+               return ret;
+       },
+
+       // Execute a callback for every element in the matched set.
+       // (You can seed the arguments with an array of args, but this is
+       // only used internally.)
+       each: function( callback, args ) {
+               return jQuery.each( this, callback, args );
+       },
+
+       ready: function( fn ) {
+               // Add the callback
+               jQuery.ready.promise().done( fn );
+
+               return this;
+       },
+
+       eq: function( i ) {
+               i = +i;
+               return i === -1 ?
+                       this.slice( i ) :
+                       this.slice( i, i + 1 );
+       },
+
+       first: function() {
+               return this.eq( 0 );
+       },
+
+       last: function() {
+               return this.eq( -1 );
+       },
+
+       slice: function() {
+               return this.pushStack( core_slice.apply( this, arguments ),
+                       "slice", core_slice.call(arguments).join(",") );
+       },
+
+       map: function( callback ) {
+               return this.pushStack( jQuery.map(this, function( elem, i ) {
+                       return callback.call( elem, i, elem );
+               }));
+       },
+
+       end: function() {
+               return this.prevObject || this.constructor(null);
+       },
+
+       // For internal use only.
+       // Behaves like an Array's method, not like a jQuery method.
+       push: core_push,
+       sort: [].sort,
+       splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+       var options, name, src, copy, copyIsArray, clone,
+               target = arguments[0] || {},
+               i = 1,
+               length = arguments.length,
+               deep = false;
+
+       // Handle a deep copy situation
+       if ( typeof target === "boolean" ) {
+               deep = target;
+               target = arguments[1] || {};
+               // skip the boolean and the target
+               i = 2;
+       }
+
+       // Handle case when target is a string or something (possible in deep copy)
+       if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+               target = {};
+       }
+
+       // extend jQuery itself if only one argument is passed
+       if ( length === i ) {
+               target = this;
+               --i;
+       }
+
+       for ( ; i < length; i++ ) {
+               // Only deal with non-null/undefined values
+               if ( (options = arguments[ i ]) != null ) {
+                       // Extend the base object
+                       for ( name in options ) {
+                               src = target[ name ];
+                               copy = options[ name ];
+
+                               // Prevent never-ending loop
+                               if ( target === copy ) {
+                                       continue;
+                               }
+
+                               // Recurse if we're merging plain objects or arrays
+                               if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+                                       if ( copyIsArray ) {
+                                               copyIsArray = false;
+                                               clone = src && jQuery.isArray(src) ? src : [];
+
+                                       } else {
+                                               clone = src && jQuery.isPlainObject(src) ? src : {};
+                                       }
+
+                                       // Never move original objects, clone them
+                                       target[ name ] = jQuery.extend( deep, clone, copy );
+
+                               // Don't bring in undefined values
+                               } else if ( copy !== undefined ) {
+                                       target[ name ] = copy;
+                               }
+                       }
+               }
+       }
+
+       // Return the modified object
+       return target;
+};
+
+jQuery.extend({
+       noConflict: function( deep ) {
+               if ( window.$ === jQuery ) {
+                       window.$ = _$;
+               }
+
+               if ( deep && window.jQuery === jQuery ) {
+                       window.jQuery = _jQuery;
+               }
+
+               return jQuery;
+       },
+
+       // Is the DOM ready to be used? Set to true once it occurs.
+       isReady: false,
+
+       // A counter to track how many items to wait for before
+       // the ready event fires. See #6781
+       readyWait: 1,
+
+       // Hold (or release) the ready event
+       holdReady: function( hold ) {
+               if ( hold ) {
+                       jQuery.readyWait++;
+               } else {
+                       jQuery.ready( true );
+               }
+       },
+
+       // Handle when the DOM is ready
+       ready: function( wait ) {
+
+               // Abort if there are pending holds or we're already ready
+               if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+                       return;
+               }
+
+               // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+               if ( !document.body ) {
+                       return setTimeout( jQuery.ready, 1 );
+               }
+
+               // Remember that the DOM is ready
+               jQuery.isReady = true;
+
+               // If a normal DOM Ready event fired, decrement, and wait if need be
+               if ( wait !== true && --jQuery.readyWait > 0 ) {
+                       return;
+               }
+
+               // If there are functions bound, to execute
+               readyList.resolveWith( document, [ jQuery ] );
+
+               // Trigger any bound ready events
+               if ( jQuery.fn.trigger ) {
+                       jQuery( document ).trigger("ready").off("ready");
+               }
+       },
+
+       // See test/unit/core.js for details concerning isFunction.
+       // Since version 1.3, DOM methods and functions like alert
+       // aren't supported. They return false on IE (#2968).
+       isFunction: function( obj ) {
+               return jQuery.type(obj) === "function";
+       },
+
+       isArray: Array.isArray || function( obj ) {
+               return jQuery.type(obj) === "array";
+       },
+
+       isWindow: function( obj ) {
+               return obj != null && obj == obj.window;
+       },
+
+       isNumeric: function( obj ) {
+               return !isNaN( parseFloat(obj) ) && isFinite( obj );
+       },
+
+       type: function( obj ) {
+               return obj == null ?
+                       String( obj ) :
+                       class2type[ core_toString.call(obj) ] || "object";
+       },
+
+       isPlainObject: function( obj ) {
+               // Must be an Object.
+               // Because of IE, we also have to check the presence of the constructor property.
+               // Make sure that DOM nodes and window objects don't pass through, as well
+               if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+                       return false;
+               }
+
+               try {
+                       // Not own constructor property must be Object
+                       if ( obj.constructor &&
+                               !core_hasOwn.call(obj, "constructor") &&
+                               !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+                               return false;
+                       }
+               } catch ( e ) {
+                       // IE8,9 Will throw exceptions on certain host objects #9897
+                       return false;
+               }
+
+               // Own properties are enumerated firstly, so to speed up,
+               // if last one is own, then all properties are own.
+
+               var key;
+               for ( key in obj ) {}
+
+               return key === undefined || core_hasOwn.call( obj, key );
+       },
+
+       isEmptyObject: function( obj ) {
+               var name;
+               for ( name in obj ) {
+                       return false;
+               }
+               return true;
+       },
+
+       error: function( msg ) {
+               throw new Error( msg );
+       },
+
+       // data: string of html
+       // context (optional): If specified, the fragment will be created in this context, defaults to document
+       // scripts (optional): If true, will include scripts passed in the html string
+       parseHTML: function( data, context, scripts ) {
+               var parsed;
+               if ( !data || typeof data !== "string" ) {
+                       return null;
+               }
+               if ( typeof context === "boolean" ) {
+                       scripts = context;
+                       context = 0;
+               }
+               context = context || document;
+
+               // Single tag
+               if ( (parsed = rsingleTag.exec( data )) ) {
+                       return [ context.createElement( parsed[1] ) ];
+               }
+
+               parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
+               return jQuery.merge( [],
+                       (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
+       },
+
+       parseJSON: function( data ) {
+               if ( !data || typeof data !== "string") {
+                       return null;
+               }
+
+               // Make sure leading/trailing whitespace is removed (IE can't handle it)
+               data = jQuery.trim( data );
+
+               // Attempt to parse using the native JSON parser first
+               if ( window.JSON && window.JSON.parse ) {
+                       return window.JSON.parse( data );
+               }
+
+               // Make sure the incoming data is actual JSON
+               // Logic borrowed from http://json.org/json2.js
+               if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+                       .replace( rvalidtokens, "]" )
+                       .replace( rvalidbraces, "")) ) {
+
+                       return ( new Function( "return " + data ) )();
+
+               }
+               jQuery.error( "Invalid JSON: " + data );
+       },
+
+       // Cross-browser xml parsing
+       parseXML: function( data ) {
+               var xml, tmp;
+               if ( !data || typeof data !== "string" ) {
+                       return null;
+               }
+               try {
+                       if ( window.DOMParser ) { // Standard
+                               tmp = new DOMParser();
+                               xml = tmp.parseFromString( data , "text/xml" );
+                       } else { // IE
+                               xml = new ActiveXObject( "Microsoft.XMLDOM" );
+                               xml.async = "false";
+                               xml.loadXML( data );
+                       }
+               } catch( e ) {
+                       xml = undefined;
+               }
+               if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+                       jQuery.error( "Invalid XML: " + data );
+               }
+               return xml;
+       },
+
+       noop: function() {},
+
+       // Evaluates a script in a global context
+       // Workarounds based on findings by Jim Driscoll
+       // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+       globalEval: function( data ) {
+               if ( data && core_rnotwhite.test( data ) ) {
+                       // We use execScript on Internet Explorer
+                       // We use an anonymous function so that context is window
+                       // rather than jQuery in Firefox
+                       ( window.execScript || function( data ) {
+                               window[ "eval" ].call( window, data );
+                       } )( data );
+               }
+       },
+
+       // Convert dashed to camelCase; used by the css and data modules
+       // Microsoft forgot to hump their vendor prefix (#9572)
+       camelCase: function( string ) {
+               return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+       },
+
+       nodeName: function( elem, name ) {
+               return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+       },
+
+       // args is for internal usage only
+       each: function( obj, callback, args ) {
+               var name,
+                       i = 0,
+                       length = obj.length,
+                       isObj = length === undefined || jQuery.isFunction( obj );
+
+               if ( args ) {
+                       if ( isObj ) {
+                               for ( name in obj ) {
+                                       if ( callback.apply( obj[ name ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.apply( obj[ i++ ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+               // A special, fast, case for the most common use of each
+               } else {
+                       if ( isObj ) {
+                               for ( name in obj ) {
+                                       if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               return obj;
+       },
+
+       // Use native String.trim function wherever possible
+       trim: core_trim ?
+               function( text ) {
+                       return text == null ?
+                               "" :
+                               core_trim.call( text );
+               } :
+
+               // Otherwise use our own trimming functionality
+               function( text ) {
+                       return text == null ?
+                               "" :
+                               text.toString().replace( rtrim, "" );
+               },
+
+       // results is for internal usage only
+       makeArray: function( arr, results ) {
+               var type,
+                       ret = results || [];
+
+               if ( arr != null ) {
+                       // The window, strings (and functions) also have 'length'
+                       // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+                       type = jQuery.type( arr );
+
+                       if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
+                               core_push.call( ret, arr );
+                       } else {
+                               jQuery.merge( ret, arr );
+                       }
+               }
+
+               return ret;
+       },
+
+       inArray: function( elem, arr, i ) {
+               var len;
+
+               if ( arr ) {
+                       if ( core_indexOf ) {
+                               return core_indexOf.call( arr, elem, i );
+                       }
+
+                       len = arr.length;
+                       i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+                       for ( ; i < len; i++ ) {
+                               // Skip accessing in sparse arrays
+                               if ( i in arr && arr[ i ] === elem ) {
+                                       return i;
+                               }
+                       }
+               }
+
+               return -1;
+       },
+
+       merge: function( first, second ) {
+               var l = second.length,
+                       i = first.length,
+                       j = 0;
+
+               if ( typeof l === "number" ) {
+                       for ( ; j < l; j++ ) {
+                               first[ i++ ] = second[ j ];
+                       }
+
+               } else {
+                       while ( second[j] !== undefined ) {
+                               first[ i++ ] = second[ j++ ];
+                       }
+               }
+
+               first.length = i;
+
+               return first;
+       },
+
+       grep: function( elems, callback, inv ) {
+               var retVal,
+                       ret = [],
+                       i = 0,
+                       length = elems.length;
+               inv = !!inv;
+
+               // Go through the array, only saving the items
+               // that pass the validator function
+               for ( ; i < length; i++ ) {
+                       retVal = !!callback( elems[ i ], i );
+                       if ( inv !== retVal ) {
+                               ret.push( elems[ i ] );
+                       }
+               }
+
+               return ret;
+       },
+
+       // arg is for internal usage only
+       map: function( elems, callback, arg ) {
+               var value, key,
+                       ret = [],
+                       i = 0,
+                       length = elems.length,
+                       // jquery objects are treated as arrays
+                       isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+               // Go through the array, translating each of the items to their
+               if ( isArray ) {
+                       for ( ; i < length; i++ ) {
+                               value = callback( elems[ i ], i, arg );
+
+                               if ( value != null ) {
+                                       ret[ ret.length ] = value;
+                               }
+                       }
+
+               // Go through every key on the object,
+               } else {
+                       for ( key in elems ) {
+                               value = callback( elems[ key ], key, arg );
+
+                               if ( value != null ) {
+                                       ret[ ret.length ] = value;
+                               }
+                       }
+               }
+
+               // Flatten any nested arrays
+               return ret.concat.apply( [], ret );
+       },
+
+       // A global GUID counter for objects
+       guid: 1,
+
+       // Bind a function to a context, optionally partially applying any
+       // arguments.
+       proxy: function( fn, context ) {
+               var tmp, args, proxy;
+
+               if ( typeof context === "string" ) {
+                       tmp = fn[ context ];
+                       context = fn;
+                       fn = tmp;
+               }
+
+               // Quick check to determine if target is callable, in the spec
+               // this throws a TypeError, but we will just return undefined.
+               if ( !jQuery.isFunction( fn ) ) {
+                       return undefined;
+               }
+
+               // Simulated bind
+               args = core_slice.call( arguments, 2 );
+               proxy = function() {
+                       return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
+               };
+
+               // Set the guid of unique handler to the same of original handler, so it can be removed
+               proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+               return proxy;
+       },
+
+       // Multifunctional method to get and set values of a collection
+       // The value/s can optionally be executed if it's a function
+       access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+               var exec,
+                       bulk = key == null,
+                       i = 0,
+                       length = elems.length;
+
+               // Sets many values
+               if ( key && typeof key === "object" ) {
+                       for ( i in key ) {
+                               jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+                       }
+                       chainable = 1;
+
+               // Sets one value
+               } else if ( value !== undefined ) {
+                       // Optionally, function values get executed if exec is true
+                       exec = pass === undefined && jQuery.isFunction( value );
+
+                       if ( bulk ) {
+                               // Bulk operations only iterate when executing function values
+                               if ( exec ) {
+                                       exec = fn;
+                                       fn = function( elem, key, value ) {
+                                               return exec.call( jQuery( elem ), value );
+                                       };
+
+                               // Otherwise they run against the entire set
+                               } else {
+                                       fn.call( elems, value );
+                                       fn = null;
+                               }
+                       }
+
+                       if ( fn ) {
+                               for (; i < length; i++ ) {
+                                       fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+                               }
+                       }
+
+                       chainable = 1;
+               }
+
+               return chainable ?
+                       elems :
+
+                       // Gets
+                       bulk ?
+                               fn.call( elems ) :
+                               length ? fn( elems[0], key ) : emptyGet;
+       },
+
+       now: function() {
+               return ( new Date() ).getTime();
+       }
+});
+
+jQuery.ready.promise = function( obj ) {
+       if ( !readyList ) {
+
+               readyList = jQuery.Deferred();
+
+               // Catch cases where $(document).ready() is called after the
+               // browser event has already occurred.
+               if ( document.readyState === "complete" || ( document.readyState !== "loading" && document.addEventListener ) ) {
+                       // Handle it asynchronously to allow scripts the opportunity to delay ready
+                       setTimeout( jQuery.ready, 1 );
+
+               // Standards-based browsers support DOMContentLoaded
+               } else if ( document.addEventListener ) {
+                       // Use the handy event callback
+                       document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+                       // A fallback to window.onload, that will always work
+                       window.addEventListener( "load", jQuery.ready, false );
+
+               // If IE event model is used
+               } else {
+                       // Ensure firing before onload, maybe late but safe also for iframes
+                       document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+                       // A fallback to window.onload, that will always work
+                       window.attachEvent( "onload", jQuery.ready );
+
+                       // If IE and not a frame
+                       // continually check to see if the document is ready
+                       var top = false;
+
+                       try {
+                               top = window.frameElement == null && document.documentElement;
+                       } catch(e) {}
+
+                       if ( top && top.doScroll ) {
+                               (function doScrollCheck() {
+                                       if ( !jQuery.isReady ) {
+
+                                               try {
+                                                       // Use the trick by Diego Perini
+                                                       // http://javascript.nwbox.com/IEContentLoaded/
+                                                       top.doScroll("left");
+                                               } catch(e) {
+                                                       return setTimeout( doScrollCheck, 50 );
+                                               }
+
+                                               // and execute any waiting functions
+                                               jQuery.ready();
+                                       }
+                               })();
+                       }
+               }
+       }
+       return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+       class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+       var object = optionsCache[ options ] = {};
+       jQuery.each( options.split( core_rspace ), function( _, flag ) {
+               object[ flag ] = true;
+       });
+       return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *     options: an optional list of space-separated options that will change how
+ *                     the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *     once:                   will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *     memory:                 will keep track of previous values and will call any callback added
+ *                                     after the list has been fired right away with the latest "memorized"
+ *                                     values (like a Deferred)
+ *
+ *     unique:                 will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *     stopOnFalse:    interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+       // Convert options from String-formatted to Object-formatted if needed
+       // (we check in cache first)
+       options = typeof options === "string" ?
+               ( optionsCache[ options ] || createOptions( options ) ) :
+               jQuery.extend( {}, options );
+
+       var // Last fire value (for non-forgettable lists)
+               memory,
+               // Flag to know if list was already fired
+               fired,
+               // Flag to know if list is currently firing
+               firing,
+               // First callback to fire (used internally by add and fireWith)
+               firingStart,
+               // End of the loop when firing
+               firingLength,
+               // Index of currently firing callback (modified by remove if needed)
+               firingIndex,
+               // Actual callback list
+               list = [],
+               // Stack of fire calls for repeatable lists
+               stack = !options.once && [],
+               // Fire callbacks
+               fire = function( data ) {
+                       memory = options.memory && data;
+                       fired = true;
+                       firingIndex = firingStart || 0;
+                       firingStart = 0;
+                       firingLength = list.length;
+                       firing = true;
+                       for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+                               if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+                                       memory = false; // To prevent further calls using add
+                                       break;
+                               }
+                       }
+                       firing = false;
+                       if ( list ) {
+                               if ( stack ) {
+                                       if ( stack.length ) {
+                                               fire( stack.shift() );
+                                       }
+                               } else if ( memory ) {
+                                       list = [];
+                               } else {
+                                       self.disable();
+                               }
+                       }
+               },
+               // Actual Callbacks object
+               self = {
+                       // Add a callback or a collection of callbacks to the list
+                       add: function() {
+                               if ( list ) {
+                                       // First, we save the current length
+                                       var start = list.length;
+                                       (function add( args ) {
+                                               jQuery.each( args, function( _, arg ) {
+                                                       if ( jQuery.isFunction( arg ) && ( !options.unique || !self.has( arg ) ) ) {
+                                                               list.push( arg );
+                                                       } else if ( arg && arg.length ) {
+                                                               // Inspect recursively
+                                                               add( arg );
+                                                       }
+                                               });
+                                       })( arguments );
+                                       // Do we need to add the callbacks to the
+                                       // current firing batch?
+                                       if ( firing ) {
+                                               firingLength = list.length;
+                                       // With memory, if we're not firing then
+                                       // we should call right away
+                                       } else if ( memory ) {
+                                               firingStart = start;
+                                               fire( memory );
+                                       }
+                               }
+                               return this;
+                       },
+                       // Remove a callback from the list
+                       remove: function() {
+                               if ( list ) {
+                                       jQuery.each( arguments, function( _, arg ) {
+                                               var index;
+                                               while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+                                                       list.splice( index, 1 );
+                                                       // Handle firing indexes
+                                                       if ( firing ) {
+                                                               if ( index <= firingLength ) {
+                                                                       firingLength--;
+                                                               }
+                                                               if ( index <= firingIndex ) {
+                                                                       firingIndex--;
+                                                               }
+                                                       }
+                                               }
+                                       });
+                               }
+                               return this;
+                       },
+                       // Control if a given callback is in the list
+                       has: function( fn ) {
+                               return jQuery.inArray( fn, list ) > -1;
+                       },
+                       // Remove all callbacks from the list
+                       empty: function() {
+                               list = [];
+                               return this;
+                       },
+                       // Have the list do nothing anymore
+                       disable: function() {
+                               list = stack = memory = undefined;
+                               return this;
+                       },
+                       // Is it disabled?
+                       disabled: function() {
+                               return !list;
+                       },
+                       // Lock the list in its current state
+                       lock: function() {
+                               stack = undefined;
+                               if ( !memory ) {
+                                       self.disable();
+                               }
+                               return this;
+                       },
+                       // Is it locked?
+                       locked: function() {
+                               return !stack;
+                       },
+                       // Call all callbacks with the given context and arguments
+                       fireWith: function( context, args ) {
+                               args = args || [];
+                               args = [ context, args.slice ? args.slice() : args ];
+                               if ( list && ( !fired || stack ) ) {
+                                       if ( firing ) {
+                                               stack.push( args );
+                                       } else {
+                                               fire( args );
+                                       }
+                               }
+                               return this;
+                       },
+                       // Call all the callbacks with the given arguments
+                       fire: function() {
+                               self.fireWith( this, arguments );
+                               return this;
+                       },
+                       // To know if the callbacks have already been called at least once
+                       fired: function() {
+                               return !!fired;
+                       }
+               };
+
+       return self;
+};
+jQuery.extend({
+
+       Deferred: function( func ) {
+               var tuples = [
+                               // action, add listener, listener list, final state
+                               [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+                               [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+                               [ "notify", "progress", jQuery.Callbacks("memory") ]
+                       ],
+                       state = "pending",
+                       promise = {
+                               state: function() {
+                                       return state;
+                               },
+                               always: function() {
+                                       deferred.done( arguments ).fail( arguments );
+                                       return this;
+                               },
+                               then: function( /* fnDone, fnFail, fnProgress */ ) {
+                                       var fns = arguments;
+                                       return jQuery.Deferred(function( newDefer ) {
+                                               jQuery.each( tuples, function( i, tuple ) {
+                                                       var action = tuple[ 0 ],
+                                                               fn = fns[ i ];
+                                                       // deferred[ done | fail | progress ] for forwarding actions to newDefer
+                                                       deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
+                                                               function() {
+                                                                       var returned = fn.apply( this, arguments );
+                                                                       if ( returned && jQuery.isFunction( returned.promise ) ) {
+                                                                               returned.promise()
+                                                                                       .done( newDefer.resolve )
+                                                                                       .fail( newDefer.reject )
+                                                                                       .progress( newDefer.notify );
+                                                                       } else {
+                                                                               newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+                                                                       }
+                                                               } :
+                                                               newDefer[ action ]
+                                                       );
+                                               });
+                                               fns = null;
+                                       }).promise();
+                               },
+                               // Get a promise for this deferred
+                               // If obj is provided, the promise aspect is added to the object
+                               promise: function( obj ) {
+                                       return typeof obj === "object" ? jQuery.extend( obj, promise ) : promise;
+                               }
+                       },
+                       deferred = {};
+
+               // Keep pipe for back-compat
+               promise.pipe = promise.then;
+
+               // Add list-specific methods
+               jQuery.each( tuples, function( i, tuple ) {
+                       var list = tuple[ 2 ],
+                               stateString = tuple[ 3 ];
+
+                       // promise[ done | fail | progress ] = list.add
+                       promise[ tuple[1] ] = list.add;
+
+                       // Handle state
+                       if ( stateString ) {
+                               list.add(function() {
+                                       // state = [ resolved | rejected ]
+                                       state = stateString;
+
+                               // [ reject_list | resolve_list ].disable; progress_list.lock
+                               }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+                       }
+
+                       // deferred[ resolve | reject | notify ] = list.fire
+                       deferred[ tuple[0] ] = list.fire;
+                       deferred[ tuple[0] + "With" ] = list.fireWith;
+               });
+
+               // Make the deferred a promise
+               promise.promise( deferred );
+
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred, deferred );
+               }
+
+               // All done!
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( subordinate /* , ..., subordinateN */ ) {
+               var i = 0,
+                       resolveValues = core_slice.call( arguments ),
+                       length = resolveValues.length,
+
+                       // the count of uncompleted subordinates
+                       remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+                       // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+                       deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+                       // Update function for both resolve and progress values
+                       updateFunc = function( i, contexts, values ) {
+                               return function( value ) {
+                                       contexts[ i ] = this;
+                                       values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+                                       if( values === progressValues ) {
+                                               deferred.notifyWith( contexts, values );
+                                       } else if ( !( --remaining ) ) {
+                                               deferred.resolveWith( contexts, values );
+                                       }
+                               };
+                       },
+
+                       progressValues, progressContexts, resolveContexts;
+
+               // add listeners to Deferred subordinates; treat others as resolved
+               if ( length > 1 ) {
+                       progressValues = new Array( length );
+                       progressContexts = new Array( length );
+                       resolveContexts = new Array( length );
+                       for ( ; i < length; i++ ) {
+                               if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+                                       resolveValues[ i ].promise()
+                                               .done( updateFunc( i, resolveContexts, resolveValues ) )
+                                               .fail( deferred.reject )
+                                               .progress( updateFunc( i, progressContexts, progressValues ) );
+                               } else {
+                                       --remaining;
+                               }
+                       }
+               }
+
+               // if we're not waiting on anything, resolve the master
+               if ( !remaining ) {
+                       deferred.resolveWith( resolveContexts, resolveValues );
+               }
+
+               return deferred.promise();
+       }
+});
+jQuery.support = (function() {
+
+       var support,
+               all,
+               a,
+               select,
+               opt,
+               input,
+               fragment,
+               eventName,
+               i,
+               isSupported,
+               clickFn,
+               div = document.createElement("div");
+
+       // Preliminary tests
+       div.setAttribute( "className", "t" );
+       div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+       all = div.getElementsByTagName("*");
+       a = div.getElementsByTagName("a")[ 0 ];
+       a.style.cssText = "top:1px;float:left;opacity:.5";
+
+       // Can't get basic test support
+       if ( !all || !all.length || !a ) {
+               return {};
+       }
+
+       // First batch of supports tests
+       select = document.createElement("select");
+       opt = select.appendChild( document.createElement("option") );
+       input = div.getElementsByTagName("input")[ 0 ];
+
+       support = {
+               // IE strips leading whitespace when .innerHTML is used
+               leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+               // Make sure that tbody elements aren't automatically inserted
+               // IE will insert them into empty tables
+               tbody: !div.getElementsByTagName("tbody").length,
+
+               // Make sure that link elements get serialized correctly by innerHTML
+               // This requires a wrapper element in IE
+               htmlSerialize: !!div.getElementsByTagName("link").length,
+
+               // Get the style information from getAttribute
+               // (IE uses .cssText instead)
+               style: /top/.test( a.getAttribute("style") ),
+
+               // Make sure that URLs aren't manipulated
+               // (IE normalizes it by default)
+               hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+               // Make sure that element opacity exists
+               // (IE uses filter instead)
+               // Use a regex to work around a WebKit issue. See #5145
+               opacity: /^0.5/.test( a.style.opacity ),
+
+               // Verify style float existence
+               // (IE uses styleFloat instead of cssFloat)
+               cssFloat: !!a.style.cssFloat,
+
+               // Make sure that if no value is specified for a checkbox
+               // that it defaults to "on".
+               // (WebKit defaults to "" instead)
+               checkOn: ( input.value === "on" ),
+
+               // Make sure that a selected-by-default option has a working selected property.
+               // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+               optSelected: opt.selected,
+
+               // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+               getSetAttribute: div.className !== "t",
+
+               // Tests for enctype support on a form(#6743)
+               enctype: !!document.createElement("form").enctype,
+
+               // Makes sure cloning an html5 element does not cause problems
+               // Where outerHTML is undefined, this still works
+               html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+               // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+               boxModel: ( document.compatMode === "CSS1Compat" ),
+
+               // Will be defined later
+               submitBubbles: true,
+               changeBubbles: true,
+               focusinBubbles: false,
+               deleteExpando: true,
+               noCloneEvent: true,
+               inlineBlockNeedsLayout: false,
+               shrinkWrapBlocks: false,
+               reliableMarginRight: true,
+               boxSizingReliable: true,
+               pixelPosition: false
+       };
+
+       // Make sure checked status is properly cloned
+       input.checked = true;
+       support.noCloneChecked = input.cloneNode( true ).checked;
+
+       // Make sure that the options inside disabled selects aren't marked as disabled
+       // (WebKit marks them as disabled)
+       select.disabled = true;
+       support.optDisabled = !opt.disabled;
+
+       // Test to see if it's possible to delete an expando from an element
+       // Fails in Internet Explorer
+       try {
+               delete div.test;
+       } catch( e ) {
+               support.deleteExpando = false;
+       }
+
+       if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+               div.attachEvent( "onclick", clickFn = function() {
+                       // Cloning a node shouldn't copy over any
+                       // bound event handlers (IE does this)
+                       support.noCloneEvent = false;
+               });
+               div.cloneNode( true ).fireEvent("onclick");
+               div.detachEvent( "onclick", clickFn );
+       }
+
+       // Check if a radio maintains its value
+       // after being appended to the DOM
+       input = document.createElement("input");
+       input.value = "t";
+       input.setAttribute( "type", "radio" );
+       support.radioValue = input.value === "t";
+
+       input.setAttribute( "checked", "checked" );
+
+       // #11217 - WebKit loses check when the name is after the checked attribute
+       input.setAttribute( "name", "t" );
+
+       div.appendChild( input );
+       fragment = document.createDocumentFragment();
+       fragment.appendChild( div.lastChild );
+
+       // WebKit doesn't clone checked state correctly in fragments
+       support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+       // Check if a disconnected checkbox will retain its checked
+       // value of true after appended to the DOM (IE6/7)
+       support.appendChecked = input.checked;
+
+       fragment.removeChild( input );
+       fragment.appendChild( div );
+
+       // Technique from Juriy Zaytsev
+       // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+       // We only care about the case where non-standard event systems
+       // are used, namely in IE. Short-circuiting here helps us to
+       // avoid an eval call (in setAttribute) which can cause CSP
+       // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+       if ( div.attachEvent ) {
+               for ( i in {
+                       submit: true,
+                       change: true,
+                       focusin: true
+               }) {
+                       eventName = "on" + i;
+                       isSupported = ( eventName in div );
+                       if ( !isSupported ) {
+                               div.setAttribute( eventName, "return;" );
+                               isSupported = ( typeof div[ eventName ] === "function" );
+                       }
+                       support[ i + "Bubbles" ] = isSupported;
+               }
+       }
+
+       // Run tests that need a body at doc ready
+       jQuery(function() {
+               var container, div, tds, marginDiv,
+                       divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
+                       body = document.getElementsByTagName("body")[0];
+
+               if ( !body ) {
+                       // Return for frameset docs that don't have a body
+                       return;
+               }
+
+               container = document.createElement("div");
+               container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
+               body.insertBefore( container, body.firstChild );
+
+               // Construct the test element
+               div = document.createElement("div");
+               container.appendChild( div );
+
+               // Check if table cells still have offsetWidth/Height when they are set
+               // to display:none and there are still other visible table cells in a
+               // table row; if so, offsetWidth/Height are not reliable for use when
+               // determining if an element has been hidden directly using
+               // display:none (it is still safe to use offsets if a parent element is
+               // hidden; don safety goggles and see bug #4512 for more information).
+               // (only IE 8 fails this test)
+               div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+               tds = div.getElementsByTagName("td");
+               tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+               isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+               tds[ 0 ].style.display = "";
+               tds[ 1 ].style.display = "none";
+
+               // Check if empty table cells still have offsetWidth/Height
+               // (IE <= 8 fail this test)
+               support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+               // Check box-sizing and margin behavior
+               div.innerHTML = "";
+               div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+               support.boxSizing = ( div.offsetWidth === 4 );
+               support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+               // NOTE: To any future maintainer, window.getComputedStyle was used here
+               // instead of getComputedStyle because it gave a better gzip size.
+               // The difference between window.getComputedStyle and getComputedStyle is
+               // 7 bytes
+               if ( window.getComputedStyle ) {
+                       support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+                       support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+                       // Check if div with explicit width and no margin-right incorrectly
+                       // gets computed margin-right based on width of container. For more
+                       // info see bug #3333
+                       // Fails in WebKit before Feb 2011 nightlies
+                       // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+                       marginDiv = document.createElement("div");
+                       marginDiv.style.cssText = div.style.cssText = divReset;
+                       marginDiv.style.marginRight = marginDiv.style.width = "0";
+                       div.style.width = "1px";
+                       div.appendChild( marginDiv );
+                       support.reliableMarginRight =
+                               !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+               }
+
+               if ( typeof div.style.zoom !== "undefined" ) {
+                       // Check if natively block-level elements act like inline-block
+                       // elements when setting their display to 'inline' and giving
+                       // them layout
+                       // (IE < 8 does this)
+                       div.innerHTML = "";
+                       div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+                       support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+                       // Check if elements with layout shrink-wrap their children
+                       // (IE 6 does this)
+                       div.style.display = "block";
+                       div.style.overflow = "visible";
+                       div.innerHTML = "<div></div>";
+                       div.firstChild.style.width = "5px";
+                       support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+                       container.style.zoom = 1;
+               }
+
+               // Null elements to avoid leaks in IE
+               body.removeChild( container );
+               container = div = tds = marginDiv = null;
+       });
+
+       // Null elements to avoid leaks in IE
+       fragment.removeChild( div );
+       all = a = select = opt = input = fragment = div = null;
+
+       return support;
+})();
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+       rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+       cache: {},
+
+       deletedIds: [],
+
+       // Please use with caution
+       uuid: 0,
+
+       // Unique for each copy of jQuery on the page
+       // Non-digits removed to match rinlinejQuery
+       expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+       // The following elements throw uncatchable exceptions if you
+       // attempt to add expando properties to them.
+       noData: {
+               "embed": true,
+               // Ban all objects except for Flash (which handle expandos)
+               "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+               "applet": true
+       },
+
+       hasData: function( elem ) {
+               elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+               return !!elem && !isEmptyDataObject( elem );
+       },
+
+       data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+               if ( !jQuery.acceptData( elem ) ) {
+                       return;
+               }
+
+               var thisCache, ret,
+                       internalKey = jQuery.expando,
+                       getByName = typeof name === "string",
+
+                       // We have to handle DOM nodes and JS objects differently because IE6-7
+                       // can't GC object references properly across the DOM-JS boundary
+                       isNode = elem.nodeType,
+
+                       // Only DOM nodes need the global jQuery cache; JS object data is
+                       // attached directly to the object so GC can occur automatically
+                       cache = isNode ? jQuery.cache : elem,
+
+                       // Only defining an ID for JS objects if its cache already exists allows
+                       // the code to shortcut on the same path as a DOM node with no cache
+                       id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+               // Avoid doing any more work than we need to when trying to get data on an
+               // object that has no data at all
+               if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
+                       return;
+               }
+
+               if ( !id ) {
+                       // Only DOM nodes need a new unique ID for each element since their data
+                       // ends up in the global cache
+                       if ( isNode ) {
+                               elem[ internalKey ] = id = jQuery.deletedIds.pop() || ++jQuery.uuid;
+                       } else {
+                               id = internalKey;
+                       }
+               }
+
+               if ( !cache[ id ] ) {
+                       cache[ id ] = {};
+
+                       // Avoids exposing jQuery metadata on plain JS objects when the object
+                       // is serialized using JSON.stringify
+                       if ( !isNode ) {
+                               cache[ id ].toJSON = jQuery.noop;
+                       }
+               }
+
+               // An object can be passed to jQuery.data instead of a key/value pair; this gets
+               // shallow copied over onto the existing cache
+               if ( typeof name === "object" || typeof name === "function" ) {
+                       if ( pvt ) {
+                               cache[ id ] = jQuery.extend( cache[ id ], name );
+                       } else {
+                               cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+                       }
+               }
+
+               thisCache = cache[ id ];
+
+               // jQuery data() is stored in a separate object inside the object's internal data
+               // cache in order to avoid key collisions between internal data and user-defined
+               // data.
+               if ( !pvt ) {
+                       if ( !thisCache.data ) {
+                               thisCache.data = {};
+                       }
+
+                       thisCache = thisCache.data;
+               }
+
+               if ( data !== undefined ) {
+                       thisCache[ jQuery.camelCase( name ) ] = data;
+               }
+
+               // Check for both converted-to-camel and non-converted data property names
+               // If a data property was specified
+               if ( getByName ) {
+
+                       // First Try to find as-is property data
+                       ret = thisCache[ name ];
+
+                       // Test for null|undefined property data
+                       if ( ret == null ) {
+
+                               // Try to find the camelCased property
+                               ret = thisCache[ jQuery.camelCase( name ) ];
+                       }
+               } else {
+                       ret = thisCache;
+               }
+
+               return ret;
+       },
+
+       removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+               if ( !jQuery.acceptData( elem ) ) {
+                       return;
+               }
+
+               var thisCache, i, l,
+
+                       isNode = elem.nodeType,
+
+                       // See jQuery.data for more information
+                       cache = isNode ? jQuery.cache : elem,
+                       id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+               // If there is already no cache entry for this object, there is no
+               // purpose in continuing
+               if ( !cache[ id ] ) {
+                       return;
+               }
+
+               if ( name ) {
+
+                       thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+                       if ( thisCache ) {
+
+                               // Support array or space separated string names for data keys
+                               if ( !jQuery.isArray( name ) ) {
+
+                                       // try the string as a key before any manipulation
+                                       if ( name in thisCache ) {
+                                               name = [ name ];
+                                       } else {
+
+                                               // split the camel cased version by spaces unless a key with the spaces exists
+                                               name = jQuery.camelCase( name );
+                                               if ( name in thisCache ) {
+                                                       name = [ name ];
+                                               } else {
+                                                       name = name.split(" ");
+                                               }
+                                       }
+                               }
+
+                               for ( i = 0, l = name.length; i < l; i++ ) {
+                                       delete thisCache[ name[i] ];
+                               }
+
+                               // If there is no data left in the cache, we want to continue
+                               // and let the cache object itself get destroyed
+                               if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+                                       return;
+                               }
+                       }
+               }
+
+               // See jQuery.data for more information
+               if ( !pvt ) {
+                       delete cache[ id ].data;
+
+                       // Don't destroy the parent cache unless the internal data object
+                       // had been the only thing left in it
+                       if ( !isEmptyDataObject( cache[ id ] ) ) {
+                               return;
+                       }
+               }
+
+               // Destroy the cache
+               if ( isNode ) {
+                       jQuery.cleanData( [ elem ], true );
+
+               // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+               } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+                       delete cache[ id ];
+
+               // When all else fails, null
+               } else {
+                       cache[ id ] = null;
+               }
+       },
+
+       // For internal use only.
+       _data: function( elem, name, data ) {
+               return jQuery.data( elem, name, data, true );
+       },
+
+       // A method for determining if a DOM node can handle the data expando
+       acceptData: function( elem ) {
+               var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+               // nodes accept data unless otherwise specified; rejection can be conditional
+               return !noData || noData !== true && elem.getAttribute("classid") === noData;
+       }
+});
+
+jQuery.fn.extend({
+       data: function( key, value ) {
+               var parts, part, attr, name, l,
+                       elem = this[0],
+                       i = 0,
+                       data = null;
+
+               // Gets all values
+               if ( key === undefined ) {
+                       if ( this.length ) {
+                               data = jQuery.data( elem );
+
+                               if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+                                       attr = elem.attributes;
+                                       for ( l = attr.length; i < l; i++ ) {
+                                               name = attr[i].name;
+
+                                               if ( name.indexOf( "data-" ) === 0 ) {
+                                                       name = jQuery.camelCase( name.substring(5) );
+
+                                                       dataAttr( elem, name, data[ name ] );
+                                               }
+                                       }
+                                       jQuery._data( elem, "parsedAttrs", true );
+                               }
+                       }
+
+                       return data;
+               }
+
+               // Sets multiple values
+               if ( typeof key === "object" ) {
+                       return this.each(function() {
+                               jQuery.data( this, key );
+                       });
+               }
+
+               parts = key.split( ".", 2 );
+               parts[1] = parts[1] ? "." + parts[1] : "";
+               part = parts[1] + "!";
+
+               return jQuery.access( this, function( value ) {
+
+                       if ( value === undefined ) {
+                               data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+                               // Try to fetch any internally stored data first
+                               if ( data === undefined && elem ) {
+                                       data = jQuery.data( elem, key );
+                                       data = dataAttr( elem, key, data );
+                               }
+
+                               return data === undefined && parts[1] ?
+                                       this.data( parts[0] ) :
+                                       data;
+                       }
+
+                       parts[1] = value;
+                       this.each(function() {
+                               var self = jQuery( this );
+
+                               self.triggerHandler( "setData" + part, parts );
+                               jQuery.data( this, key, value );
+                               self.triggerHandler( "changeData" + part, parts );
+                       });
+               }, null, value, arguments.length > 1, null, false );
+       },
+
+       removeData: function( key ) {
+               return this.each(function() {
+                       jQuery.removeData( this, key );
+               });
+       }
+});
+
+function dataAttr( elem, key, data ) {
+       // If nothing was found internally, try to fetch any
+       // data from the HTML5 data-* attribute
+       if ( data === undefined && elem.nodeType === 1 ) {
+
+               var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+               data = elem.getAttribute( name );
+
+               if ( typeof data === "string" ) {
+                       try {
+                               data = data === "true" ? true :
+                               data === "false" ? false :
+                               data === "null" ? null :
+                               // Only convert to a number if it doesn't change the string
+                               +data + "" === data ? +data :
+                               rbrace.test( data ) ? jQuery.parseJSON( data ) :
+                                       data;
+                       } catch( e ) {}
+
+                       // Make sure we set the data so it isn't changed later
+                       jQuery.data( elem, key, data );
+
+               } else {
+                       data = undefined;
+               }
+       }
+
+       return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+       var name;
+       for ( name in obj ) {
+
+               // if the public data object is empty, the private is still empty
+               if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+                       continue;
+               }
+               if ( name !== "toJSON" ) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+jQuery.extend({
+       queue: function( elem, type, data ) {
+               var queue;
+
+               if ( elem ) {
+                       type = ( type || "fx" ) + "queue";
+                       queue = jQuery._data( elem, type );
+
+                       // Speed up dequeue by getting out quickly if this is just a lookup
+                       if ( data ) {
+                               if ( !queue || jQuery.isArray(data) ) {
+                                       queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+                               } else {
+                                       queue.push( data );
+                               }
+                       }
+                       return queue || [];
+               }
+       },
+
+       dequeue: function( elem, type ) {
+               type = type || "fx";
+
+               var queue = jQuery.queue( elem, type ),
+                       fn = queue.shift(),
+                       hooks = jQuery._queueHooks( elem, type ),
+                       next = function() {
+                               jQuery.dequeue( elem, type );
+                       };
+
+               // If the fx queue is dequeued, always remove the progress sentinel
+               if ( fn === "inprogress" ) {
+                       fn = queue.shift();
+               }
+
+               if ( fn ) {
+
+                       // Add a progress sentinel to prevent the fx queue from being
+                       // automatically dequeued
+                       if ( type === "fx" ) {
+                               queue.unshift( "inprogress" );
+                       }
+
+                       // clear up the last queue stop function
+                       delete hooks.stop;
+                       fn.call( elem, next, hooks );
+               }
+               if ( !queue.length && hooks ) {
+                       hooks.empty.fire();
+               }
+       },
+
+       // not intended for public consumption - generates a queueHooks object, or returns the current one
+       _queueHooks: function( elem, type ) {
+               var key = type + "queueHooks";
+               return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+                       empty: jQuery.Callbacks("once memory").add(function() {
+                               jQuery.removeData( elem, type + "queue", true );
+                               jQuery.removeData( elem, key, true );
+                       })
+               });
+       }
+});
+
+jQuery.fn.extend({
+       queue: function( type, data ) {
+               var setter = 2;
+
+               if ( typeof type !== "string" ) {
+                       data = type;
+                       type = "fx";
+                       setter--;
+               }
+
+               if ( arguments.length < setter ) {
+                       return jQuery.queue( this[0], type );
+               }
+
+               return data === undefined ?
+                       this :
+                       this.each(function() {
+                               var queue = jQuery.queue( this, type, data );
+
+                               // ensure a hooks for this queue
+                               jQuery._queueHooks( this, type );
+
+                               if ( type === "fx" && queue[0] !== "inprogress" ) {
+                                       jQuery.dequeue( this, type );
+                               }
+                       });
+       },
+       dequeue: function( type ) {
+               return this.each(function() {
+                       jQuery.dequeue( this, type );
+               });
+       },
+       // Based off of the plugin by Clint Helfers, with permission.
+       // http://blindsignals.com/index.php/2009/07/jquery-delay/
+       delay: function( time, type ) {
+               time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+               type = type || "fx";
+
+               return this.queue( type, function( next, hooks ) {
+                       var timeout = setTimeout( next, time );
+                       hooks.stop = function() {
+                               clearTimeout( timeout );
+                       };
+               });
+       },
+       clearQueue: function( type ) {
+               return this.queue( type || "fx", [] );
+       },
+       // Get a promise resolved when queues of a certain type
+       // are emptied (fx is the type by default)
+       promise: function( type, obj ) {
+               var tmp,
+                       count = 1,
+                       defer = jQuery.Deferred(),
+                       elements = this,
+                       i = this.length,
+                       resolve = function() {
+                               if ( !( --count ) ) {
+                                       defer.resolveWith( elements, [ elements ] );
+                               }
+                       };
+
+               if ( typeof type !== "string" ) {
+                       obj = type;
+                       type = undefined;
+               }
+               type = type || "fx";
+
+               while( i-- ) {
+                       if ( (tmp = jQuery._data( elements[ i ], type + "queueHooks" )) && tmp.empty ) {
+                               count++;
+                               tmp.empty.add( resolve );
+                       }
+               }
+               resolve();
+               return defer.promise( obj );
+       }
+});
+var nodeHook, boolHook, fixSpecified,
+       rclass = /[\t\r\n]/g,
+       rreturn = /\r/g,
+       rtype = /^(?:button|input)$/i,
+       rfocusable = /^(?:button|input|object|select|textarea)$/i,
+       rclickable = /^a(?:rea|)$/i,
+       rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+       getSetAttribute = jQuery.support.getSetAttribute;
+
+jQuery.fn.extend({
+       attr: function( name, value ) {
+               return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+       },
+
+       removeAttr: function( name ) {
+               return this.each(function() {
+                       jQuery.removeAttr( this, name );
+               });
+       },
+
+       prop: function( name, value ) {
+               return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+       },
+
+       removeProp: function( name ) {
+               name = jQuery.propFix[ name ] || name;
+               return this.each(function() {
+                       // try/catch handles cases where IE balks (such as removing a property on window)
+                       try {
+                               this[ name ] = undefined;
+                               delete this[ name ];
+                       } catch( e ) {}
+               });
+       },
+
+       addClass: function( value ) {
+               var classNames, i, l, elem,
+                       setClass, c, cl;
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( j ) {
+                               jQuery( this ).addClass( value.call(this, j, this.className) );
+                       });
+               }
+
+               if ( value && typeof value === "string" ) {
+                       classNames = value.split( core_rspace );
+
+                       for ( i = 0, l = this.length; i < l; i++ ) {
+                               elem = this[ i ];
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !elem.className && classNames.length === 1 ) {
+                                               elem.className = value;
+
+                                       } else {
+                                               setClass = " " + elem.className + " ";
+
+                                               for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+                                                               setClass += classNames[ c ] + " ";
+                                                       }
+                                               }
+                                               elem.className = jQuery.trim( setClass );
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       removeClass: function( value ) {
+               var removes, className, elem, c, cl, i, l;
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( j ) {
+                               jQuery( this ).removeClass( value.call(this, j, this.className) );
+                       });
+               }
+               if ( (value && typeof value === "string") || value === undefined ) {
+                       removes = ( value || "" ).split( core_rspace );
+
+                       for ( i = 0, l = this.length; i < l; i++ ) {
+                               elem = this[ i ];
+                               if ( elem.nodeType === 1 && elem.className ) {
+
+                                       className = (" " + elem.className + " ").replace( rclass, " " );
+
+                                       // loop over each item in the removal list
+                                       for ( c = 0, cl = removes.length; c < cl; c++ ) {
+                                               // Remove until there is nothing to remove,
+                                               while ( className.indexOf(" " + removes[ c ] + " ") > -1 ) {
+                                                       className = className.replace( " " + removes[ c ] + " " , " " );
+                                               }
+                                       }
+                                       elem.className = value ? jQuery.trim( className ) : "";
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       toggleClass: function( value, stateVal ) {
+               var type = typeof value,
+                       isBool = typeof stateVal === "boolean";
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function( i ) {
+                               jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+                       });
+               }
+
+               return this.each(function() {
+                       if ( type === "string" ) {
+                               // toggle individual class names
+                               var className,
+                                       i = 0,
+                                       self = jQuery( this ),
+                                       state = stateVal,
+                                       classNames = value.split( core_rspace );
+
+                               while ( (className = classNames[ i++ ]) ) {
+                                       // check each className given, space separated list
+                                       state = isBool ? state : !self.hasClass( className );
+                                       self[ state ? "addClass" : "removeClass" ]( className );
+                               }
+
+                       } else if ( type === "undefined" || type === "boolean" ) {
+                               if ( this.className ) {
+                                       // store className if set
+                                       jQuery._data( this, "__className__", this.className );
+                               }
+
+                               // toggle whole className
+                               this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+                       }
+               });
+       },
+
+       hasClass: function( selector ) {
+               var className = " " + selector + " ",
+                       i = 0,
+                       l = this.length;
+               for ( ; i < l; i++ ) {
+                       if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       },
+
+       val: function( value ) {
+               var hooks, ret, isFunction,
+                       elem = this[0];
+
+               if ( !arguments.length ) {
+                       if ( elem ) {
+                               hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+                               if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+                                       return ret;
+                               }
+
+                               ret = elem.value;
+
+                               return typeof ret === "string" ?
+                                       // handle most common string cases
+                                       ret.replace(rreturn, "") :
+                                       // handle cases where value is null/undef or number
+                                       ret == null ? "" : ret;
+                       }
+
+                       return;
+               }
+
+               isFunction = jQuery.isFunction( value );
+
+               return this.each(function( i ) {
+                       var val,
+                               self = jQuery(this);
+
+                       if ( this.nodeType !== 1 ) {
+                               return;
+                       }
+
+                       if ( isFunction ) {
+                               val = value.call( this, i, self.val() );
+                       } else {
+                               val = value;
+                       }
+
+                       // Treat null/undefined as ""; convert numbers to string
+                       if ( val == null ) {
+                               val = "";
+                       } else if ( typeof val === "number" ) {
+                               val += "";
+                       } else if ( jQuery.isArray( val ) ) {
+                               val = jQuery.map(val, function ( value ) {
+                                       return value == null ? "" : value + "";
+                               });
+                       }
+
+                       hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+                       // If set returns undefined, fall back to normal setting
+                       if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+                               this.value = val;
+                       }
+               });
+       }
+});
+
+jQuery.extend({
+       valHooks: {
+               option: {
+                       get: function( elem ) {
+                               // attributes.value is undefined in Blackberry 4.7 but
+                               // uses .value. See #6932
+                               var val = elem.attributes.value;
+                               return !val || val.specified ? elem.value : elem.text;
+                       }
+               },
+               select: {
+                       get: function( elem ) {
+                               var value, i, max, option,
+                                       index = elem.selectedIndex,
+                                       values = [],
+                                       options = elem.options,
+                                       one = elem.type === "select-one";
+
+                               // Nothing was selected
+                               if ( index < 0 ) {
+                                       return null;
+                               }
+
+                               // Loop through all the selected options
+                               i = one ? index : 0;
+                               max = one ? index + 1 : options.length;
+                               for ( ; i < max; i++ ) {
+                                       option = options[ i ];
+
+                                       // Don't return options that are disabled or in a disabled optgroup
+                                       if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+                                                       (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+                                               // Get the specific value for the option
+                                               value = jQuery( option ).val();
+
+                                               // We don't need an array for one selects
+                                               if ( one ) {
+                                                       return value;
+                                               }
+
+                                               // Multi-Selects return an array
+                                               values.push( value );
+                                       }
+                               }
+
+                               // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+                               if ( one && !values.length && options.length ) {
+                                       return jQuery( options[ index ] ).val();
+                               }
+
+                               return values;
+                       },
+
+                       set: function( elem, value ) {
+                               var values = jQuery.makeArray( value );
+
+                               jQuery(elem).find("option").each(function() {
+                                       this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+                               });
+
+                               if ( !values.length ) {
+                                       elem.selectedIndex = -1;
+                               }
+                               return values;
+                       }
+               }
+       },
+
+       // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
+       attrFn: {},
+
+       attr: function( elem, name, value, pass ) {
+               var ret, hooks, notxml,
+                       nType = elem.nodeType;
+
+               // don't get/set attributes on text, comment and attribute nodes
+               if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+                       return;
+               }
+
+               if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
+                       return jQuery( elem )[ name ]( value );
+               }
+
+               // Fallback to prop when attributes are not supported
+               if ( typeof elem.getAttribute === "undefined" ) {
+                       return jQuery.prop( elem, name, value );
+               }
+
+               notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+               // All attributes are lowercase
+               // Grab necessary hook if one is defined
+               if ( notxml ) {
+                       name = name.toLowerCase();
+                       hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+               }
+
+               if ( value !== undefined ) {
+
+                       if ( value === null ) {
+                               jQuery.removeAttr( elem, name );
+                               return;
+
+                       } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+                               return ret;
+
+                       } else {
+                               elem.setAttribute( name, "" + value );
+                               return value;
+                       }
+
+               } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+                       return ret;
+
+               } else {
+
+                       ret = elem.getAttribute( name );
+
+                       // Non-existent attributes return null, we normalize to undefined
+                       return ret === null ?
+                               undefined :
+                               ret;
+               }
+       },
+
+       removeAttr: function( elem, value ) {
+               var propName, attrNames, name, isBool,
+                       i = 0;
+
+               if ( value && elem.nodeType === 1 ) {
+
+                       attrNames = value.split( core_rspace );
+
+                       for ( ; i < attrNames.length; i++ ) {
+                               name = attrNames[ i ];
+
+                               if ( name ) {
+                                       propName = jQuery.propFix[ name ] || name;
+                                       isBool = rboolean.test( name );
+
+                                       // See #9699 for explanation of this approach (setting first, then removal)
+                                       // Do not do this for boolean attributes (see #10870)
+                                       if ( !isBool ) {
+                                               jQuery.attr( elem, name, "" );
+                                       }
+                                       elem.removeAttribute( getSetAttribute ? name : propName );
+
+                                       // Set corresponding property to false for boolean attributes
+                                       if ( isBool && propName in elem ) {
+                                               elem[ propName ] = false;
+                                       }
+                               }
+                       }
+               }
+       },
+
+       attrHooks: {
+               type: {
+                       set: function( elem, value ) {
+                               // We can't allow the type property to be changed (since it causes problems in IE)
+                               if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+                                       jQuery.error( "type property can't be changed" );
+                               } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+                                       // Setting the type on a radio button after the value resets the value in IE6-9
+                                       // Reset value to it's default in case type is set after value
+                                       // This is for element creation
+                                       var val = elem.value;
+                                       elem.setAttribute( "type", value );
+                                       if ( val ) {
+                                               elem.value = val;
+                                       }
+                                       return value;
+                               }
+                       }
+               },
+               // Use the value property for back compat
+               // Use the nodeHook for button elements in IE6/7 (#1954)
+               value: {
+                       get: function( elem, name ) {
+                               if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+                                       return nodeHook.get( elem, name );
+                               }
+                               return name in elem ?
+                                       elem.value :
+                                       null;
+                       },
+                       set: function( elem, value, name ) {
+                               if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+                                       return nodeHook.set( elem, value, name );
+                               }
+                               // Does not return so that setAttribute is also used
+                               elem.value = value;
+                       }
+               }
+       },
+
+       propFix: {
+               tabindex: "tabIndex",
+               readonly: "readOnly",
+               "for": "htmlFor",
+               "class": "className",
+               maxlength: "maxLength",
+               cellspacing: "cellSpacing",
+               cellpadding: "cellPadding",
+               rowspan: "rowSpan",
+               colspan: "colSpan",
+               usemap: "useMap",
+               frameborder: "frameBorder",
+               contenteditable: "contentEditable"
+       },
+
+       prop: function( elem, name, value ) {
+               var ret, hooks, notxml,
+                       nType = elem.nodeType;
+
+               // don't get/set properties on text, comment and attribute nodes
+               if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+                       return;
+               }
+
+               notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+               if ( notxml ) {
+                       // Fix name and attach hooks
+                       name = jQuery.propFix[ name ] || name;
+                       hooks = jQuery.propHooks[ name ];
+               }
+
+               if ( value !== undefined ) {
+                       if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+                               return ret;
+
+                       } else {
+                               return ( elem[ name ] = value );
+                       }
+
+               } else {
+                       if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+                               return ret;
+
+                       } else {
+                               return elem[ name ];
+                       }
+               }
+       },
+
+       propHooks: {
+               tabIndex: {
+                       get: function( elem ) {
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               var attributeNode = elem.getAttributeNode("tabindex");
+
+                               return attributeNode && attributeNode.specified ?
+                                       parseInt( attributeNode.value, 10 ) :
+                                       rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+                                               0 :
+                                               undefined;
+                       }
+               }
+       }
+});
+
+// Hook for boolean attributes
+boolHook = {
+       get: function( elem, name ) {
+               // Align boolean attributes with corresponding properties
+               // Fall back to attribute presence where some booleans are not supported
+               var attrNode,
+                       property = jQuery.prop( elem, name );
+               return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+                       name.toLowerCase() :
+                       undefined;
+       },
+       set: function( elem, value, name ) {
+               var propName;
+               if ( value === false ) {
+                       // Remove boolean attributes when set to false
+                       jQuery.removeAttr( elem, name );
+               } else {
+                       // value is true since we know at this point it's type boolean and not false
+                       // Set boolean attributes to the same name and set the DOM property
+                       propName = jQuery.propFix[ name ] || name;
+                       if ( propName in elem ) {
+                               // Only set the IDL specifically if it already exists on the element
+                               elem[ propName ] = true;
+                       }
+
+                       elem.setAttribute( name, name.toLowerCase() );
+               }
+               return name;
+       }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+       fixSpecified = {
+               name: true,
+               id: true,
+               coords: true
+       };
+
+       // Use this for any attribute in IE6/7
+       // This fixes almost every IE6/7 issue
+       nodeHook = jQuery.valHooks.button = {
+               get: function( elem, name ) {
+                       var ret;
+                       ret = elem.getAttributeNode( name );
+                       return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
+                               ret.value :
+                               undefined;
+               },
+               set: function( elem, value, name ) {
+                       // Set the existing or create a new attribute node
+                       var ret = elem.getAttributeNode( name );
+                       if ( !ret ) {
+                               ret = document.createAttribute( name );
+                               elem.setAttributeNode( ret );
+                       }
+                       return ( ret.value = value + "" );
+               }
+       };
+
+       // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+       // This is for removals
+       jQuery.each([ "width", "height" ], function( i, name ) {
+               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+                       set: function( elem, value ) {
+                               if ( value === "" ) {
+                                       elem.setAttribute( name, "auto" );
+                                       return value;
+                               }
+                       }
+               });
+       });
+
+       // Set contenteditable to false on removals(#10429)
+       // Setting to empty string throws an error as an invalid value
+       jQuery.attrHooks.contenteditable = {
+               get: nodeHook.get,
+               set: function( elem, value, name ) {
+                       if ( value === "" ) {
+                               value = "false";
+                       }
+                       nodeHook.set( elem, value, name );
+               }
+       };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+       jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+               jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+                       get: function( elem ) {
+                               var ret = elem.getAttribute( name, 2 );
+                               return ret === null ? undefined : ret;
+                       }
+               });
+       });
+}
+
+if ( !jQuery.support.style ) {
+       jQuery.attrHooks.style = {
+               get: function( elem ) {
+                       // Return undefined in the case of empty string
+                       // Normalize to lowercase since IE uppercases css property names
+                       return elem.style.cssText.toLowerCase() || undefined;
+               },
+               set: function( elem, value ) {
+                       return ( elem.style.cssText = "" + value );
+               }
+       };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+       jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+               get: function( elem ) {
+                       var parent = elem.parentNode;
+
+                       if ( parent ) {
+                               parent.selectedIndex;
+
+                               // Make sure that it also works with optgroups, see #5701
+                               if ( parent.parentNode ) {
+                                       parent.parentNode.selectedIndex;
+                               }
+                       }
+                       return null;
+               }
+       });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+       jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+       jQuery.each([ "radio", "checkbox" ], function() {
+               jQuery.valHooks[ this ] = {
+                       get: function( elem ) {
+                               // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+                               return elem.getAttribute("value") === null ? "on" : elem.value;
+                       }
+               };
+       });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+       jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+               set: function( elem, value ) {
+                       if ( jQuery.isArray( value ) ) {
+                               return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+                       }
+               }
+       });
+});
+var rformElems = /^(?:textarea|input|select)$/i,
+       rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
+       rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
+       rkeyEvent = /^key/,
+       rmouseEvent = /^(?:mouse|contextmenu)|click/,
+       rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+       hoverHack = function( events ) {
+               return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+       };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+       add: function( elem, types, handler, data, selector ) {
+
+               var elemData, eventHandle, events,
+                       t, tns, type, namespaces, handleObj,
+                       handleObjIn, handlers, special;
+
+               // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+               if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+                       return;
+               }
+
+               // Caller can pass in an object of custom data in lieu of the handler
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+                       selector = handleObjIn.selector;
+               }
+
+               // Make sure that the handler has a unique ID, used to find/remove it later
+               if ( !handler.guid ) {
+                       handler.guid = jQuery.guid++;
+               }
+
+               // Init the element's event structure and main handler, if this is the first
+               events = elemData.events;
+               if ( !events ) {
+                       elemData.events = events = {};
+               }
+               eventHandle = elemData.handle;
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function( e ) {
+                               // Discard the second event of a jQuery.event.trigger() and
+                               // when an event is called after a page has unloaded
+                               return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+                                       jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+                                       undefined;
+                       };
+                       // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+                       eventHandle.elem = elem;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).bind("mouseover mouseout", fn);
+               types = jQuery.trim( hoverHack(types) ).split( " " );
+               for ( t = 0; t < types.length; t++ ) {
+
+                       tns = rtypenamespace.exec( types[t] ) || [];
+                       type = tns[1];
+                       namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+                       // If event changes its type, use the special event handlers for the changed type
+                       special = jQuery.event.special[ type ] || {};
+
+                       // If selector defined, determine special event api type, otherwise given type
+                       type = ( selector ? special.delegateType : special.bindType ) || type;
+
+                       // Update special based on newly reset type
+                       special = jQuery.event.special[ type ] || {};
+
+                       // handleObj is passed to all event handlers
+                       handleObj = jQuery.extend({
+                               type: type,
+                               origType: tns[1],
+                               data: data,
+                               handler: handler,
+                               guid: handler.guid,
+                               selector: selector,
+                               namespace: namespaces.join(".")
+                       }, handleObjIn );
+
+                       // Init the event handler queue if we're the first
+                       handlers = events[ type ];
+                       if ( !handlers ) {
+                               handlers = events[ type ] = [];
+                               handlers.delegateCount = 0;
+
+                               // Only use addEventListener/attachEvent if the special events handler returns false
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+                                       // Bind the global event handler to the element
+                                       if ( elem.addEventListener ) {
+                                               elem.addEventListener( type, eventHandle, false );
+
+                                       } else if ( elem.attachEvent ) {
+                                               elem.attachEvent( "on" + type, eventHandle );
+                                       }
+                               }
+                       }
+
+                       if ( special.add ) {
+                               special.add.call( elem, handleObj );
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
+                       // Add to the element's handler list, delegates in front
+                       if ( selector ) {
+                               handlers.splice( handlers.delegateCount++, 0, handleObj );
+                       } else {
+                               handlers.push( handleObj );
+                       }
+
+                       // Keep track of which events have ever been used, for event optimization
+                       jQuery.event.global[ type ] = true;
+               }
+
+               // Nullify elem to prevent memory leaks in IE
+               elem = null;
+       },
+
+       global: {},
+
+       // Detach an event or set of events from an element
+       remove: function( elem, types, handler, selector, mappedTypes ) {
+
+               var t, tns, type, origType, namespaces, origCount,
+                       j, events, special, eventType, handleObj,
+                       elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+               if ( !elemData || !(events = elemData.events) ) {
+                       return;
+               }
+
+               // Once for each type.namespace in types; type may be omitted
+               types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+               for ( t = 0; t < types.length; t++ ) {
+                       tns = rtypenamespace.exec( types[t] ) || [];
+                       type = origType = tns[1];
+                       namespaces = tns[2];
+
+                       // Unbind all events (on this namespace, if provided) for the element
+                       if ( !type ) {
+                               for ( type in events ) {
+                                       jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+                               }
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+                       type = ( selector? special.delegateType : special.bindType ) || type;
+                       eventType = events[ type ] || [];
+                       origCount = eventType.length;
+                       namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+
+                       // Remove matching events
+                       for ( j = 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( ( mappedTypes || origType === handleObj.origType ) &&
+                                        ( !handler || handler.guid === handleObj.guid ) &&
+                                        ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+                                        ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+                                       eventType.splice( j--, 1 );
+
+                                       if ( handleObj.selector ) {
+                                               eventType.delegateCount--;
+                                       }
+                                       if ( special.remove ) {
+                                               special.remove.call( elem, handleObj );
+                                       }
+                               }
+                       }
+
+                       // Remove generic event handler if we removed something and no more handlers exist
+                       // (avoids potential for endless recursion during removal of special event handlers)
+                       if ( eventType.length === 0 && origCount !== eventType.length ) {
+                               if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+                                       jQuery.removeEvent( elem, type, elemData.handle );
+                               }
+
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       delete elemData.handle;
+
+                       // removeData also checks for emptiness and clears the expando if empty
+                       // so use it instead of delete
+                       jQuery.removeData( elem, "events", true );
+               }
+       },
+
+       // Events that are safe to short-circuit if no handlers are attached.
+       // Native DOM events should not be added, they may have inline handlers.
+       customEvent: {
+               "getData": true,
+               "setData": true,
+               "changeData": true
+       },
+
+       trigger: function( event, data, elem, onlyHandlers ) {
+               // Don't do events on text and comment nodes
+               if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+                       return;
+               }
+
+               // Event object or event type
+               var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
+                       type = event.type || event,
+                       namespaces = [];
+
+               // focus/blur morphs to focusin/out; ensure we're not firing them right now
+               if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+                       return;
+               }
+
+               if ( type.indexOf( "!" ) >= 0 ) {
+                       // Exclusive events trigger only for the exact event (no namespaces)
+                       type = type.slice(0, -1);
+                       exclusive = true;
+               }
+
+               if ( type.indexOf( "." ) >= 0 ) {
+                       // Namespaced trigger; create a regexp to match event type in handle()
+                       namespaces = type.split(".");
+                       type = namespaces.shift();
+                       namespaces.sort();
+               }
+
+               if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+                       // No jQuery handlers for this event type, and it can't have inline handlers
+                       return;
+               }
+
+               // Caller can pass in an Event, Object, or just an event type string
+               event = typeof event === "object" ?
+                       // jQuery.Event object
+                       event[ jQuery.expando ] ? event :
+                       // Object literal
+                       new jQuery.Event( type, event ) :
+                       // Just the event type (string)
+                       new jQuery.Event( type );
+
+               event.type = type;
+               event.isTrigger = true;
+               event.exclusive = exclusive;
+               event.namespace = namespaces.join( "." );
+               event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+               ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+               // Handle a global trigger
+               if ( !elem ) {
+
+                       // TODO: Stop taunting the data cache; remove global events and always attach to document
+                       cache = jQuery.cache;
+                       for ( i in cache ) {
+                               if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+                                       jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+                               }
+                       }
+                       return;
+               }
+
+               // Clean up the event in case it is being reused
+               event.result = undefined;
+               if ( !event.target ) {
+                       event.target = elem;
+               }
+
+               // Clone any incoming data and prepend the event, creating the handler arg list
+               data = data != null ? jQuery.makeArray( data ) : [];
+               data.unshift( event );
+
+               // Allow special events to draw outside the lines
+               special = jQuery.event.special[ type ] || {};
+               if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+                       return;
+               }
+
+               // Determine event propagation path in advance, per W3C events spec (#9951)
+               // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+               eventPath = [[ elem, special.bindType || type ]];
+               if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+                       bubbleType = special.delegateType || type;
+                       cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+                       for ( old = elem; cur; cur = cur.parentNode ) {
+                               eventPath.push([ cur, bubbleType ]);
+                               old = cur;
+                       }
+
+                       // Only add window if we got to document (e.g., not plain obj or detached DOM)
+                       if ( old === (elem.ownerDocument || document) ) {
+                               eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+                       }
+               }
+
+               // Fire handlers on the event path
+               for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+                       cur = eventPath[i][0];
+                       event.type = eventPath[i][1];
+
+                       handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+                       if ( handle ) {
+                               handle.apply( cur, data );
+                       }
+                       // Note that this is a bare JS function and not a jQuery handler
+                       handle = ontype && cur[ ontype ];
+                       if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
+                               event.preventDefault();
+                       }
+               }
+               event.type = type;
+
+               // If nobody prevented the default action, do it now
+               if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+                       if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+                               !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+                               // Call a native DOM method on the target with the same name name as the event.
+                               // Can't use an .isFunction() check here because IE6/7 fails that test.
+                               // Don't do default actions on window, that's where global variables be (#6170)
+                               // IE<9 dies on focus/blur to hidden element (#1486)
+                               if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+                                       // Don't re-trigger an onFOO event when we call its FOO() method
+                                       old = elem[ ontype ];
+
+                                       if ( old ) {
+                                               elem[ ontype ] = null;
+                                       }
+
+                                       // Prevent re-triggering of the same event, since we already bubbled it above
+                                       jQuery.event.triggered = type;
+                                       elem[ type ]();
+                                       jQuery.event.triggered = undefined;
+
+                                       if ( old ) {
+                                               elem[ ontype ] = old;
+                                       }
+                               }
+                       }
+               }
+
+               return event.result;
+       },
+
+       dispatch: function( event ) {
+
+               // Make a writable jQuery.Event from the native event object
+               event = jQuery.event.fix( event || window.event );
+
+               var i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related,
+                       handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+                       delegateCount = handlers.delegateCount,
+                       args = [].slice.call( arguments ),
+                       run_all = !event.exclusive && !event.namespace,
+                       special = jQuery.event.special[ event.type ] || {},
+                       handlerQueue = [];
+
+               // Use the fix-ed jQuery.Event rather than the (read-only) native event
+               args[0] = event;
+               event.delegateTarget = this;
+
+               // Call the preDispatch hook for the mapped type, and let it bail if desired
+               if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+                       return;
+               }
+
+               // Determine handlers that should run if there are delegated events
+               // Avoid non-left-click bubbling in Firefox (#3861)
+               if ( delegateCount && !(event.button && event.type === "click") ) {
+
+                       // Pregenerate a single jQuery object for reuse with .is()
+                       jqcur = jQuery(this);
+                       jqcur.context = this;
+
+                       for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+                               // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #xxxx)
+                               if ( cur.disabled !== true || event.type !== "click" ) {
+                                       selMatch = {};
+                                       matches = [];
+                                       jqcur[0] = cur;
+                                       for ( i = 0; i < delegateCount; i++ ) {
+                                               handleObj = handlers[ i ];
+                                               sel = handleObj.selector;
+
+                                               if ( selMatch[ sel ] === undefined ) {
+                                                       selMatch[ sel ] = jqcur.is( sel );
+                                               }
+                                               if ( selMatch[ sel ] ) {
+                                                       matches.push( handleObj );
+                                               }
+                                       }
+                                       if ( matches.length ) {
+                                               handlerQueue.push({ elem: cur, matches: matches });
+                                       }
+                               }
+                       }
+               }
+
+               // Add the remaining (directly-bound) handlers
+               if ( handlers.length > delegateCount ) {
+                       handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+               }
+
+               // Run delegates first; they may want to stop propagation beneath us
+               for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+                       matched = handlerQueue[ i ];
+                       event.currentTarget = matched.elem;
+
+                       for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+                               handleObj = matched.matches[ j ];
+
+                               // Triggered event must either 1) be non-exclusive and have no namespace, or
+                               // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+                               if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+
+                                       ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+                                                       .apply( matched.elem, args );
+
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // Call the postDispatch hook for the mapped type
+               if ( special.postDispatch ) {
+                       special.postDispatch.call( this, event );
+               }
+
+               return event.result;
+       },
+
+       // Includes some event props shared by KeyEvent and MouseEvent
+       // *** attrChange attrName relatedNode srcElement  are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+       props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+       fixHooks: {},
+
+       keyHooks: {
+               props: "char charCode key keyCode".split(" "),
+               filter: function( event, original ) {
+
+                       // Add which for key events
+                       if ( event.which == null ) {
+                               event.which = original.charCode != null ? original.charCode : original.keyCode;
+                       }
+
+                       return event;
+               }
+       },
+
+       mouseHooks: {
+               props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+               filter: function( event, original ) {
+                       var eventDoc, doc, body,
+                               button = original.button,
+                               fromElement = original.fromElement;
+
+                       // Calculate pageX/Y if missing and clientX/Y available
+                       if ( event.pageX == null && original.clientX != null ) {
+                               eventDoc = event.target.ownerDocument || document;
+                               doc = eventDoc.documentElement;
+                               body = eventDoc.body;
+
+                               event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+                               event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+                       }
+
+                       // Add relatedTarget, if necessary
+                       if ( !event.relatedTarget && fromElement ) {
+                               event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+                       }
+
+                       // Add which for click: 1 === left; 2 === middle; 3 === right
+                       // Note: button is not normalized, so don't use it
+                       if ( !event.which && button !== undefined ) {
+                               event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+                       }
+
+                       return event;
+               }
+       },
+
+       fix: function( event ) {
+               if ( event[ jQuery.expando ] ) {
+                       return event;
+               }
+
+               // Create a writable copy of the event object and normalize some properties
+               var i, prop,
+                       originalEvent = event,
+                       fixHook = jQuery.event.fixHooks[ event.type ] || {},
+                       copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+               event = jQuery.Event( originalEvent );
+
+               for ( i = copy.length; i; ) {
+                       prop = copy[ --i ];
+                       event[ prop ] = originalEvent[ prop ];
+               }
+
+               // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+               if ( !event.target ) {
+                       event.target = originalEvent.srcElement || document;
+               }
+
+               // Target should not be a text node (#504, Safari)
+               if ( event.target.nodeType === 3 ) {
+                       event.target = event.target.parentNode;
+               }
+
+               // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
+               event.metaKey = !!event.metaKey;
+
+               return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+       },
+
+       special: {
+               ready: {
+                       // Make sure the ready event is setup
+                       setup: jQuery.bindReady
+               },
+
+               load: {
+                       // Prevent triggered image.load events from bubbling to window.load
+                       noBubble: true
+               },
+
+               focus: {
+                       delegateType: "focusin"
+               },
+               blur: {
+                       delegateType: "focusout"
+               },
+
+               beforeunload: {
+                       setup: function( data, namespaces, eventHandle ) {
+                               // We only want to do this special case on windows
+                               if ( jQuery.isWindow( this ) ) {
+                                       this.onbeforeunload = eventHandle;
+                               }
+                       },
+
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
+                                       this.onbeforeunload = null;
+                               }
+                       }
+               }
+       },
+
+       simulate: function( type, elem, event, bubble ) {
+               // Piggyback on a donor event to simulate a different one.
+               // Fake originalEvent to avoid donor's stopPropagation, but if the
+               // simulated event prevents default then we do the same on the donor.
+               var e = jQuery.extend(
+                       new jQuery.Event(),
+                       event,
+                       { type: type,
+                               isSimulated: true,
+                               originalEvent: {}
+                       }
+               );
+               if ( bubble ) {
+                       jQuery.event.trigger( e, null, elem );
+               } else {
+                       jQuery.event.dispatch.call( elem, e );
+               }
+               if ( e.isDefaultPrevented() ) {
+                       event.preventDefault();
+               }
+       }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               if ( elem.removeEventListener ) {
+                       elem.removeEventListener( type, handle, false );
+               }
+       } :
+       function( elem, type, handle ) {
+               var name = "on" + type;
+
+               if ( elem.detachEvent ) {
+
+                       // #8545, #7054, preventing memory leaks for custom events in IE6-8 â€“
+                       // detachEvent needed property on element, by name of that event, to properly expose it to GC
+                       if ( typeof elem[ name ] === "undefined" ) {
+                               elem[ name ] = null;
+                       }
+
+                       elem.detachEvent( name, handle );
+               }
+       };
+
+jQuery.Event = function( src, props ) {
+       // Allow instantiation without the 'new' keyword
+       if ( !(this instanceof jQuery.Event) ) {
+               return new jQuery.Event( src, props );
+       }
+
+       // Event object
+       if ( src && src.type ) {
+               this.originalEvent = src;
+               this.type = src.type;
+
+               // Events bubbling up the document may have been marked as prevented
+               // by a handler lower down the tree; reflect the correct value.
+               this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+                       src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+       // Event type
+       } else {
+               this.type = src;
+       }
+
+       // Put explicitly provided properties onto the event object
+       if ( props ) {
+               jQuery.extend( this, props );
+       }
+
+       // Create a timestamp if incoming event doesn't have one
+       this.timeStamp = src && src.timeStamp || jQuery.now();
+
+       // Mark it as fixed
+       this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+       return false;
+}
+function returnTrue() {
+       return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+       preventDefault: function() {
+               this.isDefaultPrevented = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+
+               // if preventDefault exists run it on the original event
+               if ( e.preventDefault ) {
+                       e.preventDefault();
+
+               // otherwise set the returnValue property of the original event to false (IE)
+               } else {
+                       e.returnValue = false;
+               }
+       },
+       stopPropagation: function() {
+               this.isPropagationStopped = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               // if stopPropagation exists run it on the original event
+               if ( e.stopPropagation ) {
+                       e.stopPropagation();
+               }
+               // otherwise set the cancelBubble property of the original event to true (IE)
+               e.cancelBubble = true;
+       },
+       stopImmediatePropagation: function() {
+               this.isImmediatePropagationStopped = returnTrue;
+               this.stopPropagation();
+       },
+       isDefaultPrevented: returnFalse,
+       isPropagationStopped: returnFalse,
+       isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+}, function( orig, fix ) {
+       jQuery.event.special[ orig ] = {
+               delegateType: fix,
+               bindType: fix,
+
+               handle: function( event ) {
+                       var ret,
+                               target = this,
+                               related = event.relatedTarget,
+                               handleObj = event.handleObj,
+                               selector = handleObj.selector;
+
+                       // For mousenter/leave call the handler if related is outside the target.
+                       // NB: No relatedTarget if the mouse left/entered the browser window
+                       if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+                               event.type = handleObj.origType;
+                               ret = handleObj.handler.apply( this, arguments );
+                               event.type = fix;
+                       }
+                       return ret;
+               }
+       };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+       jQuery.event.special.submit = {
+               setup: function() {
+                       // Only need this for delegated form submit events
+                       if ( jQuery.nodeName( this, "form" ) ) {
+                               return false;
+                       }
+
+                       // Lazy-add a submit handler when a descendant form may potentially be submitted
+                       jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+                               // Node name check avoids a VML-related crash in IE (#9807)
+                               var elem = e.target,
+                                       form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+                               if ( form && !jQuery._data( form, "_submit_attached" ) ) {
+                                       jQuery.event.add( form, "submit._submit", function( event ) {
+                                               event._submit_bubble = true;
+                                       });
+                                       jQuery._data( form, "_submit_attached", true );
+                               }
+                       });
+                       // return undefined since we don't need an event listener
+               },
+
+               postDispatch: function( event ) {
+                       // If form was submitted by the user, bubble the event up the tree
+                       if ( event._submit_bubble ) {
+                               delete event._submit_bubble;
+                               if ( this.parentNode && !event.isTrigger ) {
+                                       jQuery.event.simulate( "submit", this.parentNode, event, true );
+                               }
+                       }
+               },
+
+               teardown: function() {
+                       // Only need this for delegated form submit events
+                       if ( jQuery.nodeName( this, "form" ) ) {
+                               return false;
+                       }
+
+                       // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+                       jQuery.event.remove( this, "._submit" );
+               }
+       };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+       jQuery.event.special.change = {
+
+               setup: function() {
+
+                       if ( rformElems.test( this.nodeName ) ) {
+                               // IE doesn't fire change on a check/radio until blur; trigger it on click
+                               // after a propertychange. Eat the blur-change in special.change.handle.
+                               // This still fires onchange a second time for check/radio after blur.
+                               if ( this.type === "checkbox" || this.type === "radio" ) {
+                                       jQuery.event.add( this, "propertychange._change", function( event ) {
+                                               if ( event.originalEvent.propertyName === "checked" ) {
+                                                       this._just_changed = true;
+                                               }
+                                       });
+                                       jQuery.event.add( this, "click._change", function( event ) {
+                                               if ( this._just_changed && !event.isTrigger ) {
+                                                       this._just_changed = false;
+                                               }
+                                               // Allow triggered, simulated change events (#11500)
+                                               jQuery.event.simulate( "change", this, event, true );
+                                       });
+                               }
+                               return false;
+                       }
+                       // Delegated event; lazy-add a change handler on descendant inputs
+                       jQuery.event.add( this, "beforeactivate._change", function( e ) {
+                               var elem = e.target;
+
+                               if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
+                                       jQuery.event.add( elem, "change._change", function( event ) {
+                                               if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+                                                       jQuery.event.simulate( "change", this.parentNode, event, true );
+                                               }
+                                       });
+                                       jQuery._data( elem, "_change_attached", true );
+                               }
+                       });
+               },
+
+               handle: function( event ) {
+                       var elem = event.target;
+
+                       // Swallow native change events from checkbox/radio, we already triggered them above
+                       if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+                               return event.handleObj.handler.apply( this, arguments );
+                       }
+               },
+
+               teardown: function() {
+                       jQuery.event.remove( this, "._change" );
+
+                       return rformElems.test( this.nodeName );
+               }
+       };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+       jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+               // Attach a single capturing handler while someone wants focusin/focusout
+               var attaches = 0,
+                       handler = function( event ) {
+                               jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+                       };
+
+               jQuery.event.special[ fix ] = {
+                       setup: function() {
+                               if ( attaches++ === 0 ) {
+                                       document.addEventListener( orig, handler, true );
+                               }
+                       },
+                       teardown: function() {
+                               if ( --attaches === 0 ) {
+                                       document.removeEventListener( orig, handler, true );
+                               }
+                       }
+               };
+       });
+}
+
+jQuery.fn.extend({
+
+       on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+               var origFn, type;
+
+               // Types can be a map of types/handlers
+               if ( typeof types === "object" ) {
+                       // ( types-Object, selector, data )
+                       if ( typeof selector !== "string" ) { // && selector != null
+                               // ( types-Object, data )
+                               data = data || selector;
+                               selector = undefined;
+                       }
+                       for ( type in types ) {
+                               this.on( type, selector, data, types[ type ], one );
+                       }
+                       return this;
+               }
+
+               if ( data == null && fn == null ) {
+                       // ( types, fn )
+                       fn = selector;
+                       data = selector = undefined;
+               } else if ( fn == null ) {
+                       if ( typeof selector === "string" ) {
+                               // ( types, selector, fn )
+                               fn = data;
+                               data = undefined;
+                       } else {
+                               // ( types, data, fn )
+                               fn = data;
+                               data = selector;
+                               selector = undefined;
+                       }
+               }
+               if ( fn === false ) {
+                       fn = returnFalse;
+               } else if ( !fn ) {
+                       return this;
+               }
+
+               if ( one === 1 ) {
+                       origFn = fn;
+                       fn = function( event ) {
+                               // Can use an empty set, since event contains the info
+                               jQuery().off( event );
+                               return origFn.apply( this, arguments );
+                       };
+                       // Use same guid so caller can remove using origFn
+                       fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+               }
+               return this.each( function() {
+                       jQuery.event.add( this, types, fn, data, selector );
+               });
+       },
+       one: function( types, selector, data, fn ) {
+               return this.on( types, selector, data, fn, 1 );
+       },
+       off: function( types, selector, fn ) {
+               var handleObj, type;
+               if ( types && types.preventDefault && types.handleObj ) {
+                       // ( event )  dispatched jQuery.Event
+                       handleObj = types.handleObj;
+                       jQuery( types.delegateTarget ).off(
+                               handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+                               handleObj.selector,
+                               handleObj.handler
+                       );
+                       return this;
+               }
+               if ( typeof types === "object" ) {
+                       // ( types-object [, selector] )
+                       for ( type in types ) {
+                               this.off( type, selector, types[ type ] );
+                       }
+                       return this;
+               }
+               if ( selector === false || typeof selector === "function" ) {
+                       // ( types [, fn] )
+                       fn = selector;
+                       selector = undefined;
+               }
+               if ( fn === false ) {
+                       fn = returnFalse;
+               }
+               return this.each(function() {
+                       jQuery.event.remove( this, types, fn, selector );
+               });
+       },
+
+       bind: function( types, data, fn ) {
+               return this.on( types, null, data, fn );
+       },
+       unbind: function( types, fn ) {
+               return this.off( types, null, fn );
+       },
+
+       live: function( types, data, fn ) {
+               jQuery( this.context ).on( types, this.selector, data, fn );
+               return this;
+       },
+       die: function( types, fn ) {
+               jQuery( this.context ).off( types, this.selector || "**", fn );
+               return this;
+       },
+
+       delegate: function( selector, types, data, fn ) {
+               return this.on( types, selector, data, fn );
+       },
+       undelegate: function( selector, types, fn ) {
+               // ( namespace ) or ( selector, types [, fn] )
+               return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+       },
+
+       trigger: function( type, data ) {
+               return this.each(function() {
+                       jQuery.event.trigger( type, data, this );
+               });
+       },
+       triggerHandler: function( type, data ) {
+               if ( this[0] ) {
+                       return jQuery.event.trigger( type, data, this[0], true );
+               }
+       },
+
+       toggle: function( fn ) {
+               // Save reference to arguments for access in closure
+               var args = arguments,
+                       guid = fn.guid || jQuery.guid++,
+                       i = 0,
+                       toggler = function( event ) {
+                               // Figure out which function to execute
+                               var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                               jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+                               // Make sure that clicks stop
+                               event.preventDefault();
+
+                               // and execute the function
+                               return args[ lastToggle ].apply( this, arguments ) || false;
+                       };
+
+               // link all the functions, so any of them can unbind this click handler
+               toggler.guid = guid;
+               while ( i < args.length ) {
+                       args[ i++ ].guid = guid;
+               }
+
+               return this.click( toggler );
+       },
+
+       hover: function( fnOver, fnOut ) {
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+       }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+       "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+       // Handle event binding
+       jQuery.fn[ name ] = function( data, fn ) {
+               if ( fn == null ) {
+                       fn = data;
+                       data = null;
+               }
+
+               return arguments.length > 0 ?
+                       this.on( name, null, data, fn ) :
+                       this.trigger( name );
+       };
+
+       if ( rkeyEvent.test( name ) ) {
+               jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+       }
+
+       if ( rmouseEvent.test( name ) ) {
+               jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+       }
+});
+/*!\r
+ * Sizzle CSS Selector Engine\r
+ *  Copyright 2012 jQuery Foundation and other contributors\r
+ *  Released under the MIT license\r
+ *  http://sizzlejs.com/\r
+ */\r
+(function( window, undefined ) {\r
+\r
+var cachedruns,\r
+       dirruns,\r
+       sortOrder,\r
+       siblingCheck,\r
+       assertGetIdNotName,\r
+\r
+       document = window.document,\r
+       docElem = document.documentElement,\r
+\r
+       strundefined = "undefined",\r
+       hasDuplicate = false,\r
+       baseHasDuplicate = true,\r
+       done = 0,\r
+       slice = [].slice,\r
+       push = [].push,\r
+\r
+       expando = ( "sizcache" + Math.random() ).replace( ".", "" ),\r
+\r
+       // Regex\r
+\r
+       // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace\r
+       whitespace = "[\\x20\\t\\r\\n\\f]",\r
+       // http://www.w3.org/TR/css3-syntax/#characters\r
+       characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",\r
+\r
+       // Loosely modeled on CSS identifier characters\r
+       // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)\r
+       // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier\r
+       identifier = characterEncoding.replace( "w", "w#" ),\r
+\r
+       // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors\r
+       operators = "([*^$|!~]?=)",\r
+       attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +\r
+               "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",\r
+       pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",\r
+       pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",\r
+       combinators = whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*",\r
+       groups = "(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|" + attributes + "|" + pseudos.replace( 2, 7 ) + "|[^\\\\(),])+",\r
+\r
+       // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\r
+       rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),\r
+\r
+       rcombinators = new RegExp( "^" + combinators ),\r
+\r
+       // All simple (non-comma) selectors, excluding insignifant trailing whitespace\r
+       rgroups = new RegExp( groups + "?(?=" + whitespace + "*,|$)", "g" ),\r
+\r
+       // A selector, or everything after leading whitespace\r
+       // Optionally followed in either case by a ")" for terminating sub-selectors\r
+       rselector = new RegExp( "^(?:(?!,)(?:(?:^|,)" + whitespace + "*" + groups + ")*?|" + whitespace + "*(.*?))(\\)|$)" ),\r
+\r
+       // All combinators and selector components (attribute test, tag, pseudo, etc.), the latter appearing together when consecutive\r
+       rtokens = new RegExp( groups.slice( 19, -6 ) + "\\x20\\t\\r\\n\\f>+~])+|" + combinators, "g" ),\r
+\r
+       // Easily-parseable/retrievable ID or TAG or CLASS selectors\r
+       rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,\r
+\r
+       rsibling = /[\x20\t\r\n\f]*[+~]/,\r
+       rendsWithNot = /:not\($/,\r
+\r
+       rheader = /h\d/i,\r
+       rinputs = /input|select|textarea|button/i,\r
+\r
+       rbackslash = /\\(?!\\)/g,\r
+\r
+       matchExpr = {\r
+               "ID": new RegExp( "^#(" + characterEncoding + ")" ),\r
+               "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),\r
+               "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),\r
+               "TAG": new RegExp( "^(" + characterEncoding.replace( "[-", "[-\\*" ) + ")" ),\r
+               "ATTR": new RegExp( "^" + attributes ),\r
+               "PSEUDO": new RegExp( "^" + pseudos ),\r
+               "CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace +\r
+                       "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +\r
+                       "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),\r
+               "POS": new RegExp( pos, "ig" ),\r
+               // For use in libraries implementing .is()\r
+               "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )\r
+       },\r
+\r
+       classCache = {},\r
+       cachedClasses = [],\r
+       compilerCache = {},\r
+       cachedSelectors = [],\r
+\r
+       // Mark a function for use in filtering\r
+       markFunction = function( fn ) {\r
+               fn.sizzleFilter = true;\r
+               return fn;\r
+       },\r
+\r
+       // Returns a function to use in pseudos for input types\r
+       createInputFunction = function( type ) {\r
+               return function( elem ) {\r
+                       // Check the input's nodeName and type\r
+                       return elem.nodeName.toLowerCase() === "input" && elem.type === type;\r
+               };\r
+       },\r
+\r
+       // Returns a function to use in pseudos for buttons\r
+       createButtonFunction = function( type ) {\r
+               return function( elem ) {\r
+                       var name = elem.nodeName.toLowerCase();\r
+                       return (name === "input" || name === "button") && elem.type === type;\r
+               };\r
+       },\r
+\r
+       // Used for testing something on an element\r
+       assert = function( fn ) {\r
+               var pass = false,\r
+                       div = document.createElement("div");\r
+               try {\r
+                       pass = fn( div );\r
+               } catch (e) {}\r
+               // release memory in IE\r
+               div = null;\r
+               return pass;\r
+       },\r
+\r
+       // Check if attributes should be retrieved by attribute nodes\r
+       assertAttributes = assert(function( div ) {\r
+               div.innerHTML = "<select></select>";\r
+               var type = typeof div.lastChild.getAttribute("multiple");\r
+               // IE8 returns a string for some attributes even when not present\r
+               return type !== "boolean" && type !== "string";\r
+       }),\r
+\r
+       // Check if getElementById returns elements by name\r
+       // Check if getElementsByName privileges form controls or returns elements by ID\r
+       assertUsableName = assert(function( div ) {\r
+               // Inject content\r
+               div.id = expando + 0;\r
+               div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";\r
+               docElem.insertBefore( div, docElem.firstChild );\r
+\r
+               // Test\r
+               var pass = document.getElementsByName &&\r
+                       // buggy browsers will return fewer than the correct 2\r
+                       document.getElementsByName( expando ).length ===\r
+                       // buggy browsers will return more than the correct 0\r
+                       2 + document.getElementsByName( expando + 0 ).length;\r
+               assertGetIdNotName = !document.getElementById( expando );\r
+\r
+               // Cleanup\r
+               docElem.removeChild( div );\r
+\r
+               return pass;\r
+       }),\r
+\r
+       // Check if the browser returns only elements\r
+       // when doing getElementsByTagName("*")\r
+       assertTagNameNoComments = assert(function( div ) {\r
+               div.appendChild( document.createComment("") );\r
+               return div.getElementsByTagName("*").length === 0;\r
+       }),\r
+\r
+       // Check if getAttribute returns normalized href attributes\r
+       assertHrefNotNormalized = assert(function( div ) {\r
+               div.innerHTML = "<a href='#'></a>";\r
+               return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&\r
+                       div.firstChild.getAttribute("href") === "#";\r
+       }),\r
+\r
+       // Check if getElementsByClassName can be trusted\r
+       assertUsableClassName = assert(function( div ) {\r
+               // Opera can't find a second classname (in 9.6)\r
+               div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";\r
+               if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {\r
+                       return false;\r
+               }\r
+\r
+               // Safari caches class attributes, doesn't catch changes (in 3.2)\r
+               div.lastChild.className = "e";\r
+               return div.getElementsByClassName("e").length !== 1;\r
+       });\r
+\r
+var Sizzle = function( selector, context, results, seed ) {\r
+       results = results || [];\r
+       context = context || document;\r
+       var match, elem, xml, m,\r
+               nodeType = context.nodeType;\r
+\r
+       if ( nodeType !== 1 && nodeType !== 9 ) {\r
+               return [];\r
+       }\r
+\r
+       if ( !selector || typeof selector !== "string" ) {\r
+               return results;\r
+       }\r
+\r
+       xml = isXML( context );\r
+\r
+       if ( !xml && !seed ) {\r
+               if ( (match = rquickExpr.exec( selector )) ) {\r
+                       // Speed-up: Sizzle("#ID")\r
+                       if ( (m = match[1]) ) {\r
+                               if ( nodeType === 9 ) {\r
+                                       elem = context.getElementById( m );\r
+                                       // Check parentNode to catch when Blackberry 4.6 returns\r
+                                       // nodes that are no longer in the document #6963\r
+                                       if ( elem && elem.parentNode ) {\r
+                                               // Handle the case where IE, Opera, and Webkit return items\r
+                                               // by name instead of ID\r
+                                               if ( elem.id === m ) {\r
+                                                       results.push( elem );\r
+                                                       return results;\r
+                                               }\r
+                                       } else {\r
+                                               return results;\r
+                                       }\r
+                               } else {\r
+                                       // Context is not a document\r
+                                       if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&\r
+                                               contains( context, elem ) && elem.id === m ) {\r
+                                               results.push( elem );\r
+                                               return results;\r
+                                       }\r
+                               }\r
+\r
+                       // Speed-up: Sizzle("TAG")\r
+                       } else if ( match[2] ) {\r
+                               push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );\r
+                               return results;\r
+\r
+                       // Speed-up: Sizzle(".CLASS")\r
+                       } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {\r
+                               push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );\r
+                               return results;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // All others\r
+       return select( selector, context, results, seed, xml );\r
+};\r
+\r
+var Expr = Sizzle.selectors = {\r
+\r
+       // Can be adjusted by the user\r
+       cacheLength: 50,\r
+\r
+       match: matchExpr,\r
+\r
+       order: [ "ID", "TAG" ],\r
+\r
+       attrHandle: {},\r
+\r
+       createPseudo: markFunction,\r
+\r
+       find: {\r
+               "ID": assertGetIdNotName ?\r
+                       function( id, context, xml ) {\r
+                               if ( typeof context.getElementById !== strundefined && !xml ) {\r
+                                       var m = context.getElementById( id );\r
+                                       // Check parentNode to catch when Blackberry 4.6 returns\r
+                                       // nodes that are no longer in the document #6963\r
+                                       return m && m.parentNode ? [m] : [];\r
+                               }\r
+                       } :\r
+                       function( id, context, xml ) {\r
+                               if ( typeof context.getElementById !== strundefined && !xml ) {\r
+                                       var m = context.getElementById( id );\r
+\r
+                                       return m ?\r
+                                               m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?\r
+                                                       [m] :\r
+                                                       undefined :\r
+                                               [];\r
+                               }\r
+                       },\r
+\r
+               "TAG": assertTagNameNoComments ?\r
+                       function( tag, context ) {\r
+                               if ( typeof context.getElementsByTagName !== strundefined ) {\r
+                                       return context.getElementsByTagName( tag );\r
+                               }\r
+                       } :\r
+                       function( tag, context ) {\r
+                               var results = context.getElementsByTagName( tag );\r
+\r
+                               // Filter out possible comments\r
+                               if ( tag === "*" ) {\r
+                                       var elem,\r
+                                               tmp = [],\r
+                                               i = 0;\r
+\r
+                                       for ( ; (elem = results[i]); i++ ) {\r
+                                               if ( elem.nodeType === 1 ) {\r
+                                                       tmp.push( elem );\r
+                                               }\r
+                                       }\r
+\r
+                                       return tmp;\r
+                               }\r
+                               return results;\r
+                       }\r
+       },\r
+\r
+       relative: {\r
+               ">": { dir: "parentNode", first: true },\r
+               " ": { dir: "parentNode" },\r
+               "+": { dir: "previousSibling", first: true },\r
+               "~": { dir: "previousSibling" }\r
+       },\r
+\r
+       preFilter: {\r
+               "ATTR": function( match ) {\r
+                       match[1] = match[1].replace( rbackslash, "" );\r
+\r
+                       // Move the given value to match[3] whether quoted or unquoted\r
+                       match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );\r
+\r
+                       if ( match[2] === "~=" ) {\r
+                               match[3] = " " + match[3] + " ";\r
+                       }\r
+\r
+                       return match.slice( 0, 4 );\r
+               },\r
+\r
+               "CHILD": function( match ) {\r
+                       /* matches from matchExpr.CHILD\r
+                               1 type (only|nth|...)\r
+                               2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)\r
+                               3 xn-component of xn+y argument ([+-]?\d*n|)\r
+                               4 sign of xn-component\r
+                               5 x of xn-component\r
+                               6 sign of y-component\r
+                               7 y of y-component\r
+                       */\r
+                       match[1] = match[1].toLowerCase();\r
+\r
+                       if ( match[1] === "nth" ) {\r
+                               // nth-child requires argument\r
+                               if ( !match[2] ) {\r
+                                       Sizzle.error( match[0] );\r
+                               }\r
+\r
+                               // numeric x and y parameters for Expr.filter.CHILD\r
+                               // remember that false/true cast respectively to 0/1\r
+                               match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );\r
+                               match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );\r
+\r
+                       // other types prohibit arguments\r
+                       } else if ( match[2] ) {\r
+                               Sizzle.error( match[0] );\r
+                       }\r
+\r
+                       return match;\r
+               },\r
+\r
+               "PSEUDO": function( match ) {\r
+                       var argument,\r
+                               unquoted = match[4];\r
+\r
+                       if ( matchExpr["CHILD"].test( match[0] ) ) {\r
+                               return null;\r
+                       }\r
+\r
+                       // Relinquish our claim on characters in `unquoted` from a closing parenthesis on\r
+                       if ( unquoted && (argument = rselector.exec( unquoted )) && argument.pop() ) {\r
+\r
+                               match[0] = match[0].slice( 0, argument[0].length - unquoted.length - 1 );\r
+                               unquoted = argument[0].slice( 0, -1 );\r
+                       }\r
+\r
+                       // Quoted or unquoted, we have the full argument\r
+                       // Return only captures needed by the pseudo filter method (type and argument)\r
+                       match.splice( 2, 3, unquoted || match[3] );\r
+                       return match;\r
+               }\r
+       },\r
+\r
+       filter: {\r
+               "ID": assertGetIdNotName ?\r
+                       function( id ) {\r
+                               id = id.replace( rbackslash, "" );\r
+                               return function( elem ) {\r
+                                       return elem.getAttribute("id") === id;\r
+                               };\r
+                       } :\r
+                       function( id ) {\r
+                               id = id.replace( rbackslash, "" );\r
+                               return function( elem ) {\r
+                                       var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");\r
+                                       return node && node.value === id;\r
+                               };\r
+                       },\r
+\r
+               "TAG": function( nodeName ) {\r
+                       if ( nodeName === "*" ) {\r
+                               return function() { return true; };\r
+                       }\r
+                       nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();\r
+\r
+                       return function( elem ) {\r
+                               return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\r
+                       };\r
+               },\r
+\r
+               "CLASS": function( className ) {\r
+                       var pattern = classCache[ className ];\r
+                       if ( !pattern ) {\r
+                               pattern = classCache[ className ] = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" );\r
+                               cachedClasses.push( className );\r
+                               // Avoid too large of a cache\r
+                               if ( cachedClasses.length > Expr.cacheLength ) {\r
+                                       delete classCache[ cachedClasses.shift() ];\r
+                               }\r
+                       }\r
+                       return function( elem ) {\r
+                               return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );\r
+                       };\r
+               },\r
+\r
+               "ATTR": function( name, operator, check ) {\r
+                       if ( !operator ) {\r
+                               return function( elem ) {\r
+                                       return Sizzle.attr( elem, name ) != null;\r
+                               };\r
+                       }\r
+\r
+                       return function( elem ) {\r
+                               var result = Sizzle.attr( elem, name ),\r
+                                       value = result + "";\r
+\r
+                               if ( result == null ) {\r
+                                       return operator === "!=";\r
+                               }\r
+\r
+                               switch ( operator ) {\r
+                                       case "=":\r
+                                               return value === check;\r
+                                       case "!=":\r
+                                               return value !== check;\r
+                                       case "^=":\r
+                                               return check && value.indexOf( check ) === 0;\r
+                                       case "*=":\r
+                                               return check && value.indexOf( check ) > -1;\r
+                                       case "$=":\r
+                                               return check && value.substr( value.length - check.length ) === check;\r
+                                       case "~=":\r
+                                               return ( " " + value + " " ).indexOf( check ) > -1;\r
+                                       case "|=":\r
+                                               return value === check || value.substr( 0, check.length + 1 ) === check + "-";\r
+                               }\r
+                       };\r
+               },\r
+\r
+               "CHILD": function( type, argument, first, last ) {\r
+\r
+                       if ( type === "nth" ) {\r
+                               var doneName = done++;\r
+\r
+                               return function( elem ) {\r
+                                       var parent, diff,\r
+                                               count = 0,\r
+                                               node = elem;\r
+\r
+                                       if ( first === 1 && last === 0 ) {\r
+                                               return true;\r
+                                       }\r
+\r
+                                       parent = elem.parentNode;\r
+\r
+                                       if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) {\r
+                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {\r
+                                                       if ( node.nodeType === 1 ) {\r
+                                                               node.sizset = ++count;\r
+                                                               if ( node === elem ) {\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                               }\r
+\r
+                                               parent[ expando ] = doneName;\r
+                                       }\r
+\r
+                                       diff = elem.sizset - last;\r
+\r
+                                       if ( first === 0 ) {\r
+                                               return diff === 0;\r
+\r
+                                       } else {\r
+                                               return ( diff % first === 0 && diff / first >= 0 );\r
+                                       }\r
+                               };\r
+                       }\r
+\r
+                       return function( elem ) {\r
+                               var node = elem;\r
+\r
+                               switch ( type ) {\r
+                                       case "only":\r
+                                       case "first":\r
+                                               while ( (node = node.previousSibling) ) {\r
+                                                       if ( node.nodeType === 1 ) {\r
+                                                               return false;\r
+                                                       }\r
+                                               }\r
+\r
+                                               if ( type === "first" ) {\r
+                                                       return true;\r
+                                               }\r
+\r
+                                               node = elem;\r
+\r
+                                               /* falls through */\r
+                                       case "last":\r
+                                               while ( (node = node.nextSibling) ) {\r
+                                                       if ( node.nodeType === 1 ) {\r
+                                                               return false;\r
+                                                       }\r
+                                               }\r
+\r
+                                               return true;\r
+                               }\r
+                       };\r
+               },\r
+\r
+               "PSEUDO": function( pseudo, argument, context, xml ) {\r
+                       // pseudo-class names are case-insensitive\r
+                       // http://www.w3.org/TR/selectors/#pseudo-classes\r
+                       // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\r
+                       var fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ];\r
+\r
+                       if ( !fn ) {\r
+                               Sizzle.error( "unsupported pseudo: " + pseudo );\r
+                       }\r
+\r
+                       // The user may set fn.sizzleFilter to indicate\r
+                       // that arguments are needed to create the filter function\r
+                       // just as Sizzle does\r
+                       if ( !fn.sizzleFilter ) {\r
+                               return fn;\r
+                       }\r
+\r
+                       return fn( argument, context, xml );\r
+               }\r
+       },\r
+\r
+       pseudos: {\r
+               "not": markFunction(function( selector, context, xml ) {\r
+                       // Trim the selector passed to compile\r
+                       // to avoid treating leading and trailing\r
+                       // spaces as combinators\r
+                       var matcher = compile( selector.replace( rtrim, "$1" ), context, xml );\r
+                       return function( elem ) {\r
+                               return !matcher( elem );\r
+                       };\r
+               }),\r
+\r
+               "enabled": function( elem ) {\r
+                       return elem.disabled === false;\r
+               },\r
+\r
+               "disabled": function( elem ) {\r
+                       return elem.disabled === true;\r
+               },\r
+\r
+               "checked": function( elem ) {\r
+                       // In CSS3, :checked should return both checked and selected elements\r
+                       // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\r
+                       var nodeName = elem.nodeName.toLowerCase();\r
+                       return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);\r
+               },\r
+\r
+               "selected": function( elem ) {\r
+                       // Accessing this property makes selected-by-default\r
+                       // options in Safari work properly\r
+                       if ( elem.parentNode ) {\r
+                               elem.parentNode.selectedIndex;\r
+                       }\r
+\r
+                       return elem.selected === true;\r
+               },\r
+\r
+               "parent": function( elem ) {\r
+                       return !Expr.pseudos["empty"]( elem );\r
+               },\r
+\r
+               "empty": function( elem ) {\r
+                       // http://www.w3.org/TR/selectors/#empty-pseudo\r
+                       // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),\r
+                       //   not comment, processing instructions, or others\r
+                       // Thanks to Diego Perini for the nodeName shortcut\r
+                       //   Greater than "@" means alpha characters (specifically not starting with "#" or "?")\r
+                       var nodeType;\r
+                       elem = elem.firstChild;\r
+                       while ( elem ) {\r
+                               if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {\r
+                                       return false;\r
+                               }\r
+                               elem = elem.nextSibling;\r
+                       }\r
+                       return true;\r
+               },\r
+\r
+               "contains": markFunction(function( text ) {\r
+                       return function( elem ) {\r
+                               return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;\r
+                       };\r
+               }),\r
+\r
+               "has": markFunction(function( selector ) {\r
+                       return function( elem ) {\r
+                               return Sizzle( selector, elem ).length > 0;\r
+                       };\r
+               }),\r
+\r
+               "header": function( elem ) {\r
+                       return rheader.test( elem.nodeName );\r
+               },\r
+\r
+               "text": function( elem ) {\r
+                       var type, attr;\r
+                       // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)\r
+                       // use getAttribute instead to test this case\r
+                       return elem.nodeName.toLowerCase() === "input" &&\r
+                               (type = elem.type) === "text" &&\r
+                               ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );\r
+               },\r
+\r
+               // Input types\r
+               "radio": createInputFunction("radio"),\r
+               "checkbox": createInputFunction("checkbox"),\r
+               "file": createInputFunction("file"),\r
+               "password": createInputFunction("password"),\r
+               "image": createInputFunction("image"),\r
+\r
+               "submit": createButtonFunction("submit"),\r
+               "reset": createButtonFunction("reset"),\r
+\r
+               "button": function( elem ) {\r
+                       var name = elem.nodeName.toLowerCase();\r
+                       return name === "input" && elem.type === "button" || name === "button";\r
+               },\r
+\r
+               "input": function( elem ) {\r
+                       return rinputs.test( elem.nodeName );\r
+               },\r
+\r
+               "focus": function( elem ) {\r
+                       var doc = elem.ownerDocument;\r
+                       return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href);\r
+               },\r
+\r
+               "active": function( elem ) {\r
+                       return elem === elem.ownerDocument.activeElement;\r
+               }\r
+       },\r
+\r
+       setFilters: {\r
+               "first": function( elements, argument, not ) {\r
+                       return not ? elements.slice( 1 ) : [ elements[0] ];\r
+               },\r
+\r
+               "last": function( elements, argument, not ) {\r
+                       var elem = elements.pop();\r
+                       return not ? elements : [ elem ];\r
+               },\r
+\r
+               "even": function( elements, argument, not ) {\r
+                       var results = [],\r
+                               i = not ? 1 : 0,\r
+                               len = elements.length;\r
+                       for ( ; i < len; i = i + 2 ) {\r
+                               results.push( elements[i] );\r
+                       }\r
+                       return results;\r
+               },\r
+\r
+               "odd": function( elements, argument, not ) {\r
+                       var results = [],\r
+                               i = not ? 0 : 1,\r
+                               len = elements.length;\r
+                       for ( ; i < len; i = i + 2 ) {\r
+                               results.push( elements[i] );\r
+                       }\r
+                       return results;\r
+               },\r
+\r
+               "lt": function( elements, argument, not ) {\r
+                       return not ? elements.slice( +argument ) : elements.slice( 0, +argument );\r
+               },\r
+\r
+               "gt": function( elements, argument, not ) {\r
+                       return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 );\r
+               },\r
+\r
+               "eq": function( elements, argument, not ) {\r
+                       var elem = elements.splice( +argument, 1 );\r
+                       return not ? elements : elem;\r
+               }\r
+       }\r
+};\r
+\r
+// Deprecated\r
+Expr.setFilters["nth"] = Expr.setFilters["eq"];\r
+\r
+// Back-compat\r
+Expr.filters = Expr.pseudos;\r
+\r
+// IE6/7 return a modified href\r
+if ( !assertHrefNotNormalized ) {\r
+       Expr.attrHandle = {\r
+               "href": function( elem ) {\r
+                       return elem.getAttribute( "href", 2 );\r
+               },\r
+               "type": function( elem ) {\r
+                       return elem.getAttribute("type");\r
+               }\r
+       };\r
+}\r
+\r
+// Add getElementsByName if usable\r
+if ( assertUsableName ) {\r
+       Expr.order.push("NAME");\r
+       Expr.find["NAME"] = function( name, context ) {\r
+               if ( typeof context.getElementsByName !== strundefined ) {\r
+                       return context.getElementsByName( name );\r
+               }\r
+       };\r
+}\r
+\r
+// Add getElementsByClassName if usable\r
+if ( assertUsableClassName ) {\r
+       Expr.order.splice( 1, 0, "CLASS" );\r
+       Expr.find["CLASS"] = function( className, context, xml ) {\r
+               if ( typeof context.getElementsByClassName !== strundefined && !xml ) {\r
+                       return context.getElementsByClassName( className );\r
+               }\r
+       };\r
+}\r
+\r
+// If slice is not available, provide a backup\r
+try {\r
+       slice.call( docElem.childNodes, 0 )[0].nodeType;\r
+} catch ( e ) {\r
+       slice = function( i ) {\r
+               var elem, results = [];\r
+               for ( ; (elem = this[i]); i++ ) {\r
+                       results.push( elem );\r
+               }\r
+               return results;\r
+       };\r
+}\r
+\r
+var isXML = Sizzle.isXML = function( elem ) {\r
+       // documentElement is verified for cases where it doesn't yet exist\r
+       // (such as loading iframes in IE - #4833)\r
+       var documentElement = elem && (elem.ownerDocument || elem).documentElement;\r
+       return documentElement ? documentElement.nodeName !== "HTML" : false;\r
+};\r
+\r
+// Element contains another\r
+var contains = Sizzle.contains = docElem.compareDocumentPosition ?\r
+       function( a, b ) {\r
+               return !!( a.compareDocumentPosition( b ) & 16 );\r
+       } :\r
+       docElem.contains ?\r
+       function( a, b ) {\r
+               var adown = a.nodeType === 9 ? a.documentElement : a,\r
+                       bup = b.parentNode;\r
+               return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );\r
+       } :\r
+       function( a, b ) {\r
+               while ( (b = b.parentNode) ) {\r
+                       if ( b === a ) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       };\r
+\r
+/**\r
+ * Utility function for retrieving the text value of an array of DOM nodes\r
+ * @param {Array|Element} elem\r
+ */\r
+var getText = Sizzle.getText = function( elem ) {\r
+       var node,\r
+               ret = "",\r
+               i = 0,\r
+               nodeType = elem.nodeType;\r
+\r
+       if ( nodeType ) {\r
+               if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\r
+                       // Use textContent for elements\r
+                       // innerText usage removed for consistency of new lines (see #11153)\r
+                       if ( typeof elem.textContent === "string" ) {\r
+                               return elem.textContent;\r
+                       } else {\r
+                               // Traverse its children\r
+                               for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\r
+                                       ret += getText( elem );\r
+                               }\r
+                       }\r
+               } else if ( nodeType === 3 || nodeType === 4 ) {\r
+                       return elem.nodeValue;\r
+               }\r
+               // Do not include comment or processing instruction nodes\r
+       } else {\r
+\r
+               // If no nodeType, this is expected to be an array\r
+               for ( ; (node = elem[i]); i++ ) {\r
+                       // Do not traverse comment nodes\r
+                       ret += getText( node );\r
+               }\r
+       }\r
+       return ret;\r
+};\r
+\r
+Sizzle.attr = function( elem, name ) {\r
+       var attr,\r
+               xml = isXML( elem );\r
+\r
+       if ( !xml ) {\r
+               name = name.toLowerCase();\r
+       }\r
+       if ( Expr.attrHandle[ name ] ) {\r
+               return Expr.attrHandle[ name ]( elem );\r
+       }\r
+       if ( assertAttributes || xml ) {\r
+               return elem.getAttribute( name );\r
+       }\r
+       attr = elem.getAttributeNode( name );\r
+       return attr ?\r
+               typeof elem[ name ] === "boolean" ?\r
+                       elem[ name ] ? name : null :\r
+                       attr.specified ? attr.value : null :\r
+               null;\r
+};\r
+\r
+Sizzle.error = function( msg ) {\r
+       throw new Error( "Syntax error, unrecognized expression: " + msg );\r
+};\r
+\r
+// Check if the JavaScript engine is using some sort of\r
+// optimization where it does not always call our comparision\r
+// function. If that is the case, discard the hasDuplicate value.\r
+//   Thus far that includes Google Chrome.\r
+[0, 0].sort(function() {\r
+       return (baseHasDuplicate = 0);\r
+});\r
+\r
+\r
+if ( docElem.compareDocumentPosition ) {\r
+       sortOrder = function( a, b ) {\r
+               if ( a === b ) {\r
+                       hasDuplicate = true;\r
+                       return 0;\r
+               }\r
+\r
+               return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?\r
+                       a.compareDocumentPosition :\r
+                       a.compareDocumentPosition(b) & 4\r
+               ) ? -1 : 1;\r
+       };\r
+\r
+} else {\r
+       sortOrder = function( a, b ) {\r
+               // The nodes are identical, we can exit early\r
+               if ( a === b ) {\r
+                       hasDuplicate = true;\r
+                       return 0;\r
+\r
+               // Fallback to using sourceIndex (in IE) if it's available on both nodes\r
+               } else if ( a.sourceIndex && b.sourceIndex ) {\r
+                       return a.sourceIndex - b.sourceIndex;\r
+               }\r
+\r
+               var al, bl,\r
+                       ap = [],\r
+                       bp = [],\r
+                       aup = a.parentNode,\r
+                       bup = b.parentNode,\r
+                       cur = aup;\r
+\r
+               // If the nodes are siblings (or identical) we can do a quick check\r
+               if ( aup === bup ) {\r
+                       return siblingCheck( a, b );\r
+\r
+               // If no parents were found then the nodes are disconnected\r
+               } else if ( !aup ) {\r
+                       return -1;\r
+\r
+               } else if ( !bup ) {\r
+                       return 1;\r
+               }\r
+\r
+               // Otherwise they're somewhere else in the tree so we need\r
+               // to build up a full list of the parentNodes for comparison\r
+               while ( cur ) {\r
+                       ap.unshift( cur );\r
+                       cur = cur.parentNode;\r
+               }\r
+\r
+               cur = bup;\r
+\r
+               while ( cur ) {\r
+                       bp.unshift( cur );\r
+                       cur = cur.parentNode;\r
+               }\r
+\r
+               al = ap.length;\r
+               bl = bp.length;\r
+\r
+               // Start walking down the tree looking for a discrepancy\r
+               for ( var i = 0; i < al && i < bl; i++ ) {\r
+                       if ( ap[i] !== bp[i] ) {\r
+                               return siblingCheck( ap[i], bp[i] );\r
+                       }\r
+               }\r
+\r
+               // We ended someplace up the tree so do a sibling check\r
+               return i === al ?\r
+                       siblingCheck( a, bp[i], -1 ) :\r
+                       siblingCheck( ap[i], b, 1 );\r
+       };\r
+\r
+       siblingCheck = function( a, b, ret ) {\r
+               if ( a === b ) {\r
+                       return ret;\r
+               }\r
+\r
+               var cur = a.nextSibling;\r
+\r
+               while ( cur ) {\r
+                       if ( cur === b ) {\r
+                               return -1;\r
+                       }\r
+\r
+                       cur = cur.nextSibling;\r
+               }\r
+\r
+               return 1;\r
+       };\r
+}\r
+\r
+// Document sorting and removing duplicates\r
+Sizzle.uniqueSort = function( results ) {\r
+       var elem,\r
+               i = 1;\r
+\r
+       if ( sortOrder ) {\r
+               hasDuplicate = baseHasDuplicate;\r
+               results.sort( sortOrder );\r
+\r
+               if ( hasDuplicate ) {\r
+                       for ( ; (elem = results[i]); i++ ) {\r
+                               if ( elem === results[ i - 1 ] ) {\r
+                                       results.splice( i--, 1 );\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return results;\r
+};\r
+\r
+function multipleContexts( selector, contexts, results, seed ) {\r
+       var i = 0,\r
+               len = contexts.length;\r
+       for ( ; i < len; i++ ) {\r
+               Sizzle( selector, contexts[i], results, seed );\r
+       }\r
+}\r
+\r
+function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) {\r
+       var results,\r
+               fn = Expr.setFilters[ posfilter.toLowerCase() ];\r
+\r
+       if ( !fn ) {\r
+               Sizzle.error( posfilter );\r
+       }\r
+\r
+       if ( selector || !(results = seed) ) {\r
+               multipleContexts( selector || "*", contexts, (results = []), seed );\r
+       }\r
+\r
+       return results.length > 0 ? fn( results, argument, not ) : [];\r
+}\r
+\r
+function handlePOS( selector, context, results, seed, groups ) {\r
+       var match, not, anchor, ret, elements, currentContexts, part, lastIndex,\r
+               i = 0,\r
+               len = groups.length,\r
+               rpos = matchExpr["POS"],\r
+               // This is generated here in case matchExpr["POS"] is extended\r
+               rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ),\r
+               // This is for making sure non-participating\r
+               // matching groups are represented cross-browser (IE6-8)\r
+               setUndefined = function() {\r
+                       var i = 1,\r
+                               len = arguments.length - 2;\r
+                       for ( ; i < len; i++ ) {\r
+                               if ( arguments[i] === undefined ) {\r
+                                       match[i] = undefined;\r
+                               }\r
+                       }\r
+               };\r
+\r
+       for ( ; i < len; i++ ) {\r
+               // Reset regex index to 0\r
+               rpos.exec("");\r
+               selector = groups[i];\r
+               ret = [];\r
+               anchor = 0;\r
+               elements = seed;\r
+               while ( (match = rpos.exec( selector )) ) {\r
+                       lastIndex = rpos.lastIndex = match.index + match[0].length;\r
+                       if ( lastIndex > anchor ) {\r
+                               part = selector.slice( anchor, match.index );\r
+                               anchor = lastIndex;\r
+                               currentContexts = [ context ];\r
+\r
+                               if ( rcombinators.test(part) ) {\r
+                                       if ( elements ) {\r
+                                               currentContexts = elements;\r
+                                       }\r
+                                       elements = seed;\r
+                               }\r
+\r
+                               if ( (not = rendsWithNot.test( part )) ) {\r
+                                       part = part.slice( 0, -5 ).replace( rcombinators, "$&*" );\r
+                               }\r
+\r
+                               if ( match.length > 1 ) {\r
+                                       match[0].replace( rposgroups, setUndefined );\r
+                               }\r
+                               elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not );\r
+                       }\r
+               }\r
+\r
+               if ( elements ) {\r
+                       ret = ret.concat( elements );\r
+\r
+                       if ( (part = selector.slice( anchor )) && part !== ")" ) {\r
+                               if ( rcombinators.test(part) ) {\r
+                                       multipleContexts( part, ret, results, seed );\r
+                               } else {\r
+                                       Sizzle( part, context, results, seed ? seed.concat(elements) : elements );\r
+                               }\r
+                       } else {\r
+                               push.apply( results, ret );\r
+                       }\r
+               } else {\r
+                       Sizzle( selector, context, results, seed );\r
+               }\r
+       }\r
+\r
+       // Do not sort if this is a single filter\r
+       return len === 1 ? results : Sizzle.uniqueSort( results );\r
+}\r
+\r
+function tokenize( selector, context, xml ) {\r
+       var tokens, soFar, type,\r
+               groups = [],\r
+               i = 0,\r
+\r
+               // Catch obvious selector issues: terminal ")"; nonempty fallback match\r
+               // rselector never fails to match *something*\r
+               match = rselector.exec( selector ),\r
+               matched = !match.pop() && !match.pop(),\r
+               selectorGroups = matched && selector.match( rgroups ) || [""],\r
+\r
+               preFilters = Expr.preFilter,\r
+               filters = Expr.filter,\r
+               checkContext = !xml && context !== document;\r
+\r
+       for ( ; (soFar = selectorGroups[i]) != null && matched; i++ ) {\r
+               groups.push( tokens = [] );\r
+\r
+               // Need to make sure we're within a narrower context if necessary\r
+               // Adding a descendant combinator will generate what is needed\r
+               if ( checkContext ) {\r
+                       soFar = " " + soFar;\r
+               }\r
+\r
+               while ( soFar ) {\r
+                       matched = false;\r
+\r
+                       // Combinators\r
+                       if ( (match = rcombinators.exec( soFar )) ) {\r
+                               soFar = soFar.slice( match[0].length );\r
+\r
+                               // Cast descendant combinators to space\r
+                               matched = tokens.push({ part: match.pop().replace( rtrim, " " ), captures: match });\r
+                       }\r
+\r
+                       // Filters\r
+                       for ( type in filters ) {\r
+                               if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||\r
+                                       (match = preFilters[ type ]( match, context, xml )) ) ) {\r
+\r
+                                       soFar = soFar.slice( match.shift().length );\r
+                                       matched = tokens.push({ part: type, captures: match });\r
+                               }\r
+                       }\r
+\r
+                       if ( !matched ) {\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       if ( !matched ) {\r
+               Sizzle.error( selector );\r
+       }\r
+\r
+       return groups;\r
+}\r
+\r
+function addCombinator( matcher, combinator, context ) {\r
+       var dir = combinator.dir,\r
+               doneName = done++;\r
+\r
+       if ( !matcher ) {\r
+               // If there is no matcher to check, check against the context\r
+               matcher = function( elem ) {\r
+                       return elem === context;\r
+               };\r
+       }\r
+       return combinator.first ?\r
+               function( elem, context ) {\r
+                       while ( (elem = elem[ dir ]) ) {\r
+                               if ( elem.nodeType === 1 ) {\r
+                                       return matcher( elem, context ) && elem;\r
+                               }\r
+                       }\r
+               } :\r
+               function( elem, context ) {\r
+                       var cache,\r
+                               dirkey = doneName + "." + dirruns,\r
+                               cachedkey = dirkey + "." + cachedruns;\r
+                       while ( (elem = elem[ dir ]) ) {\r
+                               if ( elem.nodeType === 1 ) {\r
+                                       if ( (cache = elem[ expando ]) === cachedkey ) {\r
+                                               return elem.sizset;\r
+                                       } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {\r
+                                               if ( elem.sizset ) {\r
+                                                       return elem;\r
+                                               }\r
+                                       } else {\r
+                                               elem[ expando ] = cachedkey;\r
+                                               if ( matcher( elem, context ) ) {\r
+                                                       elem.sizset = true;\r
+                                                       return elem;\r
+                                               }\r
+                                               elem.sizset = false;\r
+                                       }\r
+                               }\r
+                       }\r
+               };\r
+}\r
+\r
+function addMatcher( higher, deeper ) {\r
+       return higher ?\r
+               function( elem, context ) {\r
+                       var result = deeper( elem, context );\r
+                       return result && higher( result === true ? elem : result, context );\r
+               } :\r
+               deeper;\r
+}\r
+\r
+// ["TAG", ">", "ID", " ", "CLASS"]\r
+function matcherFromTokens( tokens, context, xml ) {\r
+       var token, matcher,\r
+               i = 0;\r
+\r
+       for ( ; (token = tokens[i]); i++ ) {\r
+               if ( Expr.relative[ token.part ] ) {\r
+                       matcher = addCombinator( matcher, Expr.relative[ token.part ], context );\r
+               } else {\r
+                       token.captures.push( context, xml );\r
+                       matcher = addMatcher( matcher, Expr.filter[ token.part ].apply( null, token.captures ) );\r
+               }\r
+       }\r
+\r
+       return matcher;\r
+}\r
+\r
+function matcherFromGroupMatchers( matchers ) {\r
+       return function( elem, context ) {\r
+               var matcher,\r
+                       j = 0;\r
+               for ( ; (matcher = matchers[j]); j++ ) {\r
+                       if ( matcher(elem, context) ) {\r
+                               return true;\r
+                       }\r
+               }\r
+               return false;\r
+       };\r
+}\r
+\r
+var compile = Sizzle.compile = function( selector, context, xml ) {\r
+       var tokens, group, i,\r
+               cached = compilerCache[ selector ];\r
+\r
+       // Return a cached group function if already generated (context dependent)\r
+       if ( cached && cached.context === context ) {\r
+               return cached;\r
+       }\r
+\r
+       // Generate a function of recursive functions that can be used to check each element\r
+       group = tokenize( selector, context, xml );\r
+       for ( i = 0; (tokens = group[i]); i++ ) {\r
+               group[i] = matcherFromTokens( tokens, context, xml );\r
+       }\r
+\r
+       // Cache the compiled function\r
+       cached = compilerCache[ selector ] = matcherFromGroupMatchers( group );\r
+       cached.context = context;\r
+       cached.runs = cached.dirruns = 0;\r
+       cachedSelectors.push( selector );\r
+       // Ensure only the most recent are cached\r
+       if ( cachedSelectors.length > Expr.cacheLength ) {\r
+               delete compilerCache[ cachedSelectors.shift() ];\r
+       }\r
+       return cached;\r
+};\r
+\r
+Sizzle.matches = function( expr, elements ) {\r
+       return Sizzle( expr, null, null, elements );\r
+};\r
+\r
+Sizzle.matchesSelector = function( elem, expr ) {\r
+       return Sizzle( expr, null, null, [ elem ] ).length > 0;\r
+};\r
+\r
+var select = function( selector, context, results, seed, xml ) {\r
+       // Remove excessive whitespace\r
+       selector = selector.replace( rtrim, "$1" );\r
+       var elements, matcher, i, len, elem, token,\r
+               type, findContext, notTokens,\r
+               match = selector.match( rgroups ),\r
+               tokens = selector.match( rtokens ),\r
+               contextNodeType = context.nodeType;\r
+\r
+       // POS handling\r
+       if ( matchExpr["POS"].test(selector) ) {\r
+               return handlePOS( selector, context, results, seed, match );\r
+       }\r
+\r
+       if ( seed ) {\r
+               elements = slice.call( seed, 0 );\r
+\r
+       // To maintain document order, only narrow the\r
+       // set if there is one group\r
+       } else if ( match && match.length === 1 ) {\r
+\r
+               // Take a shortcut and set the context if the root selector is an ID\r
+               if ( tokens.length > 1 && contextNodeType === 9 && !xml &&\r
+                               (match = matchExpr["ID"].exec( tokens[0] )) ) {\r
+\r
+                       context = Expr.find["ID"]( match[1], context, xml )[0];\r
+                       if ( !context ) {\r
+                               return results;\r
+                       }\r
+\r
+                       selector = selector.slice( tokens.shift().length );\r
+               }\r
+\r
+               findContext = ( (match = rsibling.exec( tokens[0] )) && !match.index && context.parentNode ) || context;\r
+\r
+               // Get the last token, excluding :not\r
+               notTokens = tokens.pop();\r
+               token = notTokens.split(":not")[0];\r
+\r
+               for ( i = 0, len = Expr.order.length; i < len; i++ ) {\r
+                       type = Expr.order[i];\r
+\r
+                       if ( (match = matchExpr[ type ].exec( token )) ) {\r
+                               elements = Expr.find[ type ]( (match[1] || "").replace( rbackslash, "" ), findContext, xml );\r
+\r
+                               if ( elements == null ) {\r
+                                       continue;\r
+                               }\r
+\r
+                               if ( token === notTokens ) {\r
+                                       selector = selector.slice( 0, selector.length - notTokens.length ) +\r
+                                               token.replace( matchExpr[ type ], "" );\r
+\r
+                                       if ( !selector ) {\r
+                                               push.apply( results, slice.call(elements, 0) );\r
+                                       }\r
+                               }\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Only loop over the given elements once\r
+       // If selector is empty, we're already done\r
+       if ( selector ) {\r
+               matcher = compile( selector, context, xml );\r
+               dirruns = matcher.dirruns++;\r
+\r
+               if ( elements == null ) {\r
+                       elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context );\r
+               }\r
+               for ( i = 0; (elem = elements[i]); i++ ) {\r
+                       cachedruns = matcher.runs++;\r
+                       if ( matcher(elem, context) ) {\r
+                               results.push( elem );\r
+                       }\r
+               }\r
+       }\r
+\r
+       return results;\r
+};\r
+\r
+if ( document.querySelectorAll ) {\r
+       (function() {\r
+               var disconnectedMatch,\r
+                       oldSelect = select,\r
+                       rescape = /'|\\/g,\r
+                       rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,\r
+                       rbuggyQSA = [],\r
+                       // matchesSelector(:active) reports false when true (IE9/Opera 11.5)\r
+                       // A support test would require too much code (would include document ready)\r
+                       // just skip matchesSelector for :active\r
+                       rbuggyMatches = [":active"],\r
+                       matches = docElem.matchesSelector ||\r
+                               docElem.mozMatchesSelector ||\r
+                               docElem.webkitMatchesSelector ||\r
+                               docElem.oMatchesSelector ||\r
+                               docElem.msMatchesSelector;\r
+\r
+               // Build QSA regex\r
+               // Regex strategy adopted from Diego Perini\r
+               assert(function( div ) {\r
+                       div.innerHTML = "<select><option selected></option></select>";\r
+\r
+                       // IE8 - Some boolean attributes are not treated correctly\r
+                       if ( !div.querySelectorAll("[selected]").length ) {\r
+                               rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );\r
+                       }\r
+\r
+                       // Webkit/Opera - :checked should return selected option elements\r
+                       // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\r
+                       // IE8 throws error here (do not put tests after this one)\r
+                       if ( !div.querySelectorAll(":checked").length ) {\r
+                               rbuggyQSA.push(":checked");\r
+                       }\r
+               });\r
+\r
+               assert(function( div ) {\r
+\r
+                       // Opera 10-12/IE9 - ^= $= *= and empty values\r
+                       // Should not select anything\r
+                       div.innerHTML = "<p test=''></p>";\r
+                       if ( div.querySelectorAll("[test^='']").length ) {\r
+                               rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );\r
+                       }\r
+\r
+                       // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\r
+                       // IE8 throws error here (do not put tests after this one)\r
+                       div.innerHTML = "<input type='hidden'>";\r
+                       if ( !div.querySelectorAll(":enabled").length ) {\r
+                               rbuggyQSA.push(":enabled", ":disabled");\r
+                       }\r
+               });\r
+\r
+               rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );\r
+\r
+               select = function( selector, context, results, seed, xml ) {\r
+                       // Only use querySelectorAll when not filtering,\r
+                       // when this is not xml,\r
+                       // and when no QSA bugs apply\r
+                       if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {\r
+                               if ( context.nodeType === 9 ) {\r
+                                       try {\r
+                                               push.apply( results, slice.call(context.querySelectorAll( selector ), 0) );\r
+                                               return results;\r
+                                       } catch(qsaError) {}\r
+                               // qSA works strangely on Element-rooted queries\r
+                               // We can work around this by specifying an extra ID on the root\r
+                               // and working up from there (Thanks to Andrew Dupont for the technique)\r
+                               // IE 8 doesn't work on object elements\r
+                               } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {\r
+                                       var old = context.getAttribute("id"),\r
+                                               nid = old || expando,\r
+                                               newContext = rsibling.test( selector ) && context.parentNode || context;\r
+\r
+                                       if ( old ) {\r
+                                               nid = nid.replace( rescape, "\\$&" );\r
+                                       } else {\r
+                                               context.setAttribute( "id", nid );\r
+                                       }\r
+\r
+                                       try {\r
+                                               push.apply( results, slice.call( newContext.querySelectorAll(\r
+                                                       selector.replace( rgroups, "[id='" + nid + "'] $&" )\r
+                                               ), 0 ) );\r
+                                               return results;\r
+                                       } catch(qsaError) {\r
+                                       } finally {\r
+                                               if ( !old ) {\r
+                                                       context.removeAttribute("id");\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       return oldSelect( selector, context, results, seed, xml );\r
+               };\r
+\r
+               if ( matches ) {\r
+                       assert(function( div ) {\r
+                               // Check to see if it's possible to do matchesSelector\r
+                               // on a disconnected node (IE 9)\r
+                               disconnectedMatch = matches.call( div, "div" );\r
+\r
+                               // This should fail with an exception\r
+                               // Gecko does not error, returns false instead\r
+                               try {\r
+                                       matches.call( div, "[test!='']:sizzle" );\r
+                                       rbuggyMatches.push( Expr.match.PSEUDO );\r
+                               } catch ( e ) {}\r
+                       });\r
+\r
+                       // rbuggyMatches always contains :active, so no need for a length check\r
+                       rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );\r
+\r
+                       Sizzle.matchesSelector = function( elem, expr ) {\r
+                               // Make sure that attribute selectors are quoted\r
+                               expr = expr.replace( rattributeQuotes, "='$1']" );\r
+\r
+                               // rbuggyMatches always contains :active, so no need for an existence check\r
+                               if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) {\r
+                                       try {\r
+                                               var ret = matches.call( elem, expr );\r
+\r
+                                               // IE 9's matchesSelector returns false on disconnected nodes\r
+                                               if ( ret || disconnectedMatch ||\r
+                                                               // As well, disconnected nodes are said to be in a document\r
+                                                               // fragment in IE 9\r
+                                                               elem.document && elem.document.nodeType !== 11 ) {\r
+                                                       return ret;\r
+                                               }\r
+                                       } catch(e) {}\r
+                               }\r
+\r
+                               return Sizzle( expr, null, null, [ elem ] ).length > 0;\r
+                       };\r
+               }\r
+       })();\r
+}\r
+\r
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+\r
+\r
+})( window );\r
+var runtil = /Until$/,
+       rparentsprev = /^(?:parents|prev(?:Until|All))/,
+       isSimple = /^.[^:#\[\.,]*$/,
+       rneedsContext = jQuery.expr.match.needsContext,
+       // methods guaranteed to produce a unique set when starting from a unique set
+       guaranteedUnique = {
+               children: true,
+               contents: true,
+               next: true,
+               prev: true
+       };
+
+jQuery.fn.extend({
+       find: function( selector ) {
+               var i, l, length, n, r, ret,
+                       self = this;
+
+               if ( typeof selector !== "string" ) {
+                       return jQuery( selector ).filter(function() {
+                               for ( i = 0, l = self.length; i < l; i++ ) {
+                                       if ( jQuery.contains( self[ i ], this ) ) {
+                                               return true;
+                                       }
+                               }
+                       });
+               }
+
+               ret = this.pushStack( "", "find", selector );
+
+               for ( i = 0, l = this.length; i < l; i++ ) {
+                       length = ret.length;
+                       jQuery.find( selector, this[i], ret );
+
+                       if ( i > 0 ) {
+                               // Make sure that the results are unique
+                               for ( n = length; n < ret.length; n++ ) {
+                                       for ( r = 0; r < length; r++ ) {
+                                               if ( ret[r] === ret[n] ) {
+                                                       ret.splice(n--, 1);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       has: function( target ) {
+               var i,
+                       targets = jQuery( target, this ),
+                       len = targets.length;
+
+               return this.filter(function() {
+                       for ( i = 0; i < len; i++ ) {
+                               if ( jQuery.contains( this, targets[i] ) ) {
+                                       return true;
+                               }
+                       }
+               });
+       },
+
+       not: function( selector ) {
+               return this.pushStack( winnow(this, selector, false), "not", selector);
+       },
+
+       filter: function( selector ) {
+               return this.pushStack( winnow(this, selector, true), "filter", selector );
+       },
+
+       is: function( selector ) {
+               return !!selector && (
+                       typeof selector === "string" ?
+                               // If this is a positional/relative selector, check membership in the returned set
+                               // so $("p:first").is("p:last") won't return true for a doc with two "p".
+                               rneedsContext.test( selector ) ?
+                                       jQuery( selector, this.context ).index( this[0] ) >= 0 :
+                                       jQuery.filter( selector, this ).length > 0 :
+                               this.filter( selector ).length > 0 );
+       },
+
+       closest: function( selectors, context ) {
+               var cur,
+                       i = 0,
+                       l = this.length,
+                       ret = [],
+                       pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+                               jQuery( selectors, context || this.context ) :
+                               0;
+
+               for ( ; i < l; i++ ) {
+                       cur = this[i];
+
+                       while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+                               if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+                                       ret.push( cur );
+                                       break;
+                               }
+                               cur = cur.parentNode;
+                       }
+               }
+
+               ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+               return this.pushStack( ret, "closest", selectors );
+       },
+
+       // Determine the position of an element within
+       // the matched set of elements
+       index: function( elem ) {
+
+               // No argument, return index in parent
+               if ( !elem ) {
+                       return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+               }
+
+               // index in selector
+               if ( typeof elem === "string" ) {
+                       return jQuery.inArray( this[0], jQuery( elem ) );
+               }
+
+               // Locate the position of the desired element
+               return jQuery.inArray(
+                       // If it receives a jQuery object, the first element is used
+                       elem.jquery ? elem[0] : elem, this );
+       },
+
+       add: function( selector, context ) {
+               var set = typeof selector === "string" ?
+                               jQuery( selector, context ) :
+                               jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+                       all = jQuery.merge( this.get(), set );
+
+               return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+                       all :
+                       jQuery.unique( all ) );
+       },
+
+       addBack: function( selector ) {
+               return this.add( selector == null ?
+                       this.prevObject : this.prevObject.filter(selector)
+               );
+       }
+});
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+       return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+function sibling( cur, dir ) {
+       do {
+               cur = cur[ dir ];
+       } while ( cur && cur.nodeType !== 1 );
+
+       return cur;
+}
+
+jQuery.each({
+       parent: function( elem ) {
+               var parent = elem.parentNode;
+               return parent && parent.nodeType !== 11 ? parent : null;
+       },
+       parents: function( elem ) {
+               return jQuery.dir( elem, "parentNode" );
+       },
+       parentsUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "parentNode", until );
+       },
+       next: function( elem ) {
+               return sibling( elem, "nextSibling" );
+       },
+       prev: function( elem ) {
+               return sibling( elem, "previousSibling" );
+       },
+       nextAll: function( elem ) {
+               return jQuery.dir( elem, "nextSibling" );
+       },
+       prevAll: function( elem ) {
+               return jQuery.dir( elem, "previousSibling" );
+       },
+       nextUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "nextSibling", until );
+       },
+       prevUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "previousSibling", until );
+       },
+       siblings: function( elem ) {
+               return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+       },
+       children: function( elem ) {
+               return jQuery.sibling( elem.firstChild );
+       },
+       contents: function( elem ) {
+               return jQuery.nodeName( elem, "iframe" ) ?
+                       elem.contentDocument || elem.contentWindow.document :
+                       jQuery.merge( [], elem.childNodes );
+       }
+}, function( name, fn ) {
+       jQuery.fn[ name ] = function( until, selector ) {
+               var ret = jQuery.map( this, fn, until );
+
+               if ( !runtil.test( name ) ) {
+                       selector = until;
+               }
+
+               if ( selector && typeof selector === "string" ) {
+                       ret = jQuery.filter( selector, ret );
+               }
+
+               ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+               if ( this.length > 1 && rparentsprev.test( name ) ) {
+                       ret = ret.reverse();
+               }
+
+               return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
+       };
+});
+
+jQuery.extend({
+       filter: function( expr, elems, not ) {
+               if ( not ) {
+                       expr = ":not(" + expr + ")";
+               }
+
+               return elems.length === 1 ?
+                       jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+                       jQuery.find.matches(expr, elems);
+       },
+
+       dir: function( elem, dir, until ) {
+               var matched = [],
+                       cur = elem[ dir ];
+
+               while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+                       if ( cur.nodeType === 1 ) {
+                               matched.push( cur );
+                       }
+                       cur = cur[dir];
+               }
+               return matched;
+       },
+
+       sibling: function( n, elem ) {
+               var r = [];
+
+               for ( ; n; n = n.nextSibling ) {
+                       if ( n.nodeType === 1 && n !== elem ) {
+                               r.push( n );
+                       }
+               }
+
+               return r;
+       }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+       // Can't pass null or undefined to indexOf in Firefox 4
+       // Set to 0 to skip string check
+       qualifier = qualifier || 0;
+
+       if ( jQuery.isFunction( qualifier ) ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       var retVal = !!qualifier.call( elem, i, elem );
+                       return retVal === keep;
+               });
+
+       } else if ( qualifier.nodeType ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return ( elem === qualifier ) === keep;
+               });
+
+       } else if ( typeof qualifier === "string" ) {
+               var filtered = jQuery.grep(elements, function( elem ) {
+                       return elem.nodeType === 1;
+               });
+
+               if ( isSimple.test( qualifier ) ) {
+                       return jQuery.filter(qualifier, filtered, !keep);
+               } else {
+                       qualifier = jQuery.filter( qualifier, filtered );
+               }
+       }
+
+       return jQuery.grep(elements, function( elem, i ) {
+               return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+       });
+}
+function createSafeFragment( document ) {
+       var list = nodeNames.split( "|" ),
+       safeFrag = document.createDocumentFragment();
+
+       if ( safeFrag.createElement ) {
+               while ( list.length ) {
+                       safeFrag.createElement(
+                               list.pop()
+                       );
+               }
+       }
+       return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+               "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+       rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+       rleadingWhitespace = /^\s+/,
+       rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+       rtagName = /<([\w:]+)/,
+       rtbody = /<tbody/i,
+       rhtml = /<|&#?\w+;/,
+       rnoInnerhtml = /<(?:script|style|link)/i,
+       rnocache = /<(?:script|object|embed|option|style)/i,
+       rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+       rcheckableType = /^(?:checkbox|radio)$/,
+       // checked="checked" or checked
+       rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+       rscriptType = /\/(java|ecma)script/i,
+       rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,
+       wrapMap = {
+               option: [ 1, "<select multiple='multiple'>", "</select>" ],
+               legend: [ 1, "<fieldset>", "</fieldset>" ],
+               thead: [ 1, "<table>", "</table>" ],
+               tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+               td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+               col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+               area: [ 1, "<map>", "</map>" ],
+               _default: [ 0, "", "" ]
+       },
+       safeFragment = createSafeFragment( document ),
+       fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+// unless wrapped in a div with non-breaking characters in front of it.
+if ( !jQuery.support.htmlSerialize ) {
+       wrapMap._default = [ 1, "X<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+       text: function( value ) {
+               return jQuery.access( this, function( value ) {
+                       return value === undefined ?
+                               jQuery.text( this ) :
+                               this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+               }, null, value, arguments.length );
+       },
+
+       wrapAll: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapAll( html.call(this, i) );
+                       });
+               }
+
+               if ( this[0] ) {
+                       // The elements to wrap the target around
+                       var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+                       if ( this[0].parentNode ) {
+                               wrap.insertBefore( this[0] );
+                       }
+
+                       wrap.map(function() {
+                               var elem = this;
+
+                               while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+                                       elem = elem.firstChild;
+                               }
+
+                               return elem;
+                       }).append( this );
+               }
+
+               return this;
+       },
+
+       wrapInner: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapInner( html.call(this, i) );
+                       });
+               }
+
+               return this.each(function() {
+                       var self = jQuery( this ),
+                               contents = self.contents();
+
+                       if ( contents.length ) {
+                               contents.wrapAll( html );
+
+                       } else {
+                               self.append( html );
+                       }
+               });
+       },
+
+       wrap: function( html ) {
+               var isFunction = jQuery.isFunction( html );
+
+               return this.each(function(i) {
+                       jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+               });
+       },
+
+       unwrap: function() {
+               return this.parent().each(function() {
+                       if ( !jQuery.nodeName( this, "body" ) ) {
+                               jQuery( this ).replaceWith( this.childNodes );
+                       }
+               }).end();
+       },
+
+       append: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 || this.nodeType === 11 ) {
+                               this.appendChild( elem );
+                       }
+               });
+       },
+
+       prepend: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 || this.nodeType === 11 ) {
+                               this.insertBefore( elem, this.firstChild );
+                       }
+               });
+       },
+
+       before: function() {
+               if ( !isDisconnected( this[0] ) ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this );
+                       });
+               }
+
+               if ( arguments.length ) {
+                       var set = jQuery.clean( arguments );
+                       return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
+               }
+       },
+
+       after: function() {
+               if ( !isDisconnected( this[0] ) ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this.nextSibling );
+                       });
+               }
+
+               if ( arguments.length ) {
+                       var set = jQuery.clean( arguments );
+                       return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
+               }
+       },
+
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               var elem,
+                       i = 0;
+
+               for ( ; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                       elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       empty: function() {
+               var elem,
+                       i = 0;
+
+               for ( ; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+
+               return this;
+       },
+
+       clone: function( dataAndEvents, deepDataAndEvents ) {
+               dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+               deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+               return this.map( function () {
+                       return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+               });
+       },
+
+       html: function( value ) {
+               return jQuery.access( this, function( value ) {
+                       var elem = this[0] || {},
+                               i = 0,
+                               l = this.length;
+
+                       if ( value === undefined ) {
+                               return elem.nodeType === 1 ?
+                                       elem.innerHTML.replace( rinlinejQuery, "" ) :
+                                       undefined;
+                       }
+
+                       // See if we can take a shortcut and just use innerHTML
+                       if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+                               ( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
+                               ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+                               !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+                               value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+                               try {
+                                       for (; i < l; i++ ) {
+                                               // Remove element nodes and prevent memory leaks
+                                               elem = this[i] || {};
+                                               if ( elem.nodeType === 1 ) {
+                                                       jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+                                                       elem.innerHTML = value;
+                                               }
+                                       }
+
+                                       elem = 0;
+
+                               // If using innerHTML throws an exception, use the fallback method
+                               } catch(e) {}
+                       }
+
+                       if ( elem ) {
+                               this.empty().append( value );
+                       }
+               }, null, value, arguments.length );
+       },
+
+       replaceWith: function( value ) {
+               if ( !isDisconnected( this[0] ) ) {
+                       // Make sure that the elements are removed from the DOM before they are inserted
+                       // this can help fix replacing a parent with child elements
+                       if ( jQuery.isFunction( value ) ) {
+                               return this.each(function(i) {
+                                       var self = jQuery(this), old = self.html();
+                                       self.replaceWith( value.call( this, i, old ) );
+                               });
+                       }
+
+                       if ( typeof value !== "string" ) {
+                               value = jQuery( value ).detach();
+                       }
+
+                       return this.each(function() {
+                               var next = this.nextSibling,
+                                       parent = this.parentNode;
+
+                               jQuery( this ).remove();
+
+                               if ( next ) {
+                                       jQuery(next).before( value );
+                               } else {
+                                       jQuery(parent).append( value );
+                               }
+                       });
+               }
+
+               return this.length ?
+                       this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+                       this;
+       },
+
+       detach: function( selector ) {
+               return this.remove( selector, true );
+       },
+
+       domManip: function( args, table, callback ) {
+
+               // Flatten any nested arrays
+               args = [].concat.apply( [], args );
+
+               var results, first, fragment, iNoClone,
+                       i = 0,
+                       value = args[0],
+                       scripts = [],
+                       l = this.length;
+
+               // We can't cloneNode fragments that contain checked, in WebKit
+               if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
+                       return this.each(function() {
+                               jQuery(this).domManip( args, table, callback );
+                       });
+               }
+
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               args[0] = value.call( this, i, table ? self.html() : undefined );
+                               self.domManip( args, table, callback );
+                       });
+               }
+
+               if ( this[0] ) {
+                       results = jQuery.buildFragment( args, this, scripts );
+                       fragment = results.fragment;
+                       first = fragment.firstChild;
+
+                       if ( fragment.childNodes.length === 1 ) {
+                               fragment = first;
+                       }
+
+                       if ( first ) {
+                               table = table && jQuery.nodeName( first, "tr" );
+
+                               // Use the original fragment for the last item instead of the first because it can end up
+                               // being emptied incorrectly in certain situations (#8070).
+                               // Fragments from the fragment cache must always be cloned and never used in place.
+                               for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
+                                       callback.call(
+                                               table && jQuery.nodeName( this[i], "table" ) ?
+                                                       findOrAppend( this[i], "tbody" ) :
+                                                       this[i],
+                                               i === iNoClone ?
+                                                       fragment :
+                                                       jQuery.clone( fragment, true, true )
+                                       );
+                               }
+                       }
+
+                       // Fix #11809: Avoid leaking memory
+                       fragment = first = null;
+
+                       if ( scripts.length ) {
+                               jQuery.each( scripts, function( i, elem ) {
+                                       if ( elem.src ) {
+                                               if ( jQuery.ajax ) {
+                                                       jQuery.ajax({
+                                                               url: elem.src,
+                                                               type: "GET",
+                                                               dataType: "script",
+                                                               async: false,
+                                                               global: false,
+                                                               "throws": true
+                                                       });
+                                               } else {
+                                                       jQuery.error("no ajax");
+                                               }
+                                       } else {
+                                               jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
+                                       }
+
+                                       if ( elem.parentNode ) {
+                                               elem.parentNode.removeChild( elem );
+                                       }
+                               });
+                       }
+               }
+
+               return this;
+       }
+});
+
+function findOrAppend( elem, tag ) {
+       return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
+}
+
+function cloneCopyEvent( src, dest ) {
+
+       if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+               return;
+       }
+
+       var type, i, l,
+               oldData = jQuery._data( src ),
+               curData = jQuery._data( dest, oldData ),
+               events = oldData.events;
+
+       if ( events ) {
+               delete curData.handle;
+               curData.events = {};
+
+               for ( type in events ) {
+                       for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+                               jQuery.event.add( dest, type, events[ type ][ i ] );
+                       }
+               }
+       }
+
+       // make the cloned public data object a copy from the original
+       if ( curData.data ) {
+               curData.data = jQuery.extend( {}, curData.data );
+       }
+}
+
+function cloneFixAttributes( src, dest ) {
+       var nodeName;
+
+       // We do not need to do anything for non-Elements
+       if ( dest.nodeType !== 1 ) {
+               return;
+       }
+
+       // clearAttributes removes the attributes, which we don't want,
+       // but also removes the attachEvent events, which we *do* want
+       if ( dest.clearAttributes ) {
+               dest.clearAttributes();
+       }
+
+       // mergeAttributes, in contrast, only merges back on the
+       // original attributes, not the events
+       if ( dest.mergeAttributes ) {
+               dest.mergeAttributes( src );
+       }
+
+       nodeName = dest.nodeName.toLowerCase();
+
+       if ( nodeName === "object" ) {
+               // IE6-10 improperly clones children of object elements using classid.
+               // IE10 throws NoModificationAllowedError if parent is null, #12132.
+               if ( dest.parentNode ) {
+                       dest.outerHTML = src.outerHTML;
+               }
+
+               // This path appears unavoidable for IE9. When cloning an object
+               // element in IE9, the outerHTML strategy above is not sufficient.
+               // If the src has innerHTML and the destination does not,
+               // copy the src.innerHTML into the dest.innerHTML. #10324
+               if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
+                       dest.innerHTML = src.innerHTML;
+               }
+
+       } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+               // IE6-8 fails to persist the checked state of a cloned checkbox
+               // or radio button. Worse, IE6-7 fail to give the cloned element
+               // a checked appearance if the defaultChecked value isn't also set
+
+               dest.defaultChecked = dest.checked = src.checked;
+
+               // IE6-7 get confused and end up setting the value of a cloned
+               // checkbox/radio button to an empty string instead of "on"
+               if ( dest.value !== src.value ) {
+                       dest.value = src.value;
+               }
+
+       // IE6-8 fails to return the selected option to the default selected
+       // state when cloning options
+       } else if ( nodeName === "option" ) {
+               dest.selected = src.defaultSelected;
+
+       // IE6-8 fails to set the defaultValue to the correct value when
+       // cloning other types of input fields
+       } else if ( nodeName === "input" || nodeName === "textarea" ) {
+               dest.defaultValue = src.defaultValue;
+
+       // IE blanks contents when cloning scripts
+       } else if ( nodeName === "script" && dest.text !== src.text ) {
+               dest.text = src.text;
+       }
+
+       // Event data gets referenced instead of copied if the expando
+       // gets copied too
+       dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, context, scripts ) {
+       var fragment, cacheable, cachehit,
+               first = args[ 0 ];
+
+       // Set context from what may come in as undefined or a jQuery collection or a node
+       context = context || document;
+       context = (context[0] || context).ownerDocument || context[0] || context;
+
+       // Ensure that an attr object doesn't incorrectly stand in as a document object
+       // Chrome and Firefox seem to allow this to occur and will throw exception
+       // Fixes #8950
+       if ( typeof context.createDocumentFragment === "undefined" ) {
+               context = document;
+       }
+
+       // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+       // Cloning options loses the selected state, so don't cache them
+       // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+       // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+       // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+       if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document &&
+               first.charAt(0) === "<" && !rnocache.test( first ) &&
+               (jQuery.support.checkClone || !rchecked.test( first )) &&
+               (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+               // Mark cacheable and look for a hit
+               cacheable = true;
+               fragment = jQuery.fragments[ first ];
+               cachehit = fragment !== undefined;
+       }
+
+       if ( !fragment ) {
+               fragment = context.createDocumentFragment();
+               jQuery.clean( args, context, fragment, scripts );
+
+               // Update the cache, but only store false
+               // unless this is a second parsing of the same content
+               if ( cacheable ) {
+                       jQuery.fragments[ first ] = cachehit && fragment;
+               }
+       }
+
+       return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+       appendTo: "append",
+       prependTo: "prepend",
+       insertBefore: "before",
+       insertAfter: "after",
+       replaceAll: "replaceWith"
+}, function( name, original ) {
+       jQuery.fn[ name ] = function( selector ) {
+               var elems,
+                       i = 0,
+                       ret = [],
+                       insert = jQuery( selector ),
+                       l = insert.length,
+                       parent = this.length === 1 && this[0].parentNode;
+
+               if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {
+                       insert[ original ]( this[0] );
+                       return this;
+               } else {
+                       for ( ; i < l; i++ ) {
+                               elems = ( i > 0 ? this.clone(true) : this ).get();
+                               jQuery( insert[i] )[ original ]( elems );
+                               ret = ret.concat( elems );
+                       }
+
+                       return this.pushStack( ret, name, insert.selector );
+               }
+       };
+});
+
+function getAll( elem ) {
+       if ( typeof elem.getElementsByTagName !== "undefined" ) {
+               return elem.getElementsByTagName( "*" );
+
+       } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+               return elem.querySelectorAll( "*" );
+
+       } else {
+               return [];
+       }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+       if ( rcheckableType.test( elem.type ) ) {
+               elem.defaultChecked = elem.checked;
+       }
+}
+
+jQuery.extend({
+       clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+               var srcElements,
+                       destElements,
+                       i,
+                       clone;
+
+               if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+                       clone = elem.cloneNode( true );
+
+               // IE<=8 does not properly clone detached, unknown element nodes
+               } else {
+                       fragmentDiv.innerHTML = elem.outerHTML;
+                       fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+               }
+
+               if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+                               (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+                       // IE copies events bound via attachEvent when using cloneNode.
+                       // Calling detachEvent on the clone will also remove the events
+                       // from the original. In order to get around this, we use some
+                       // proprietary methods to clear the events. Thanks to MooTools
+                       // guys for this hotness.
+
+                       cloneFixAttributes( elem, clone );
+
+                       // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+                       srcElements = getAll( elem );
+                       destElements = getAll( clone );
+
+                       // Weird iteration because IE will replace the length property
+                       // with an element if you are cloning the body and one of the
+                       // elements on the page has a name or id of "length"
+                       for ( i = 0; srcElements[i]; ++i ) {
+                               // Ensure that the destination node is not null; Fixes #9587
+                               if ( destElements[i] ) {
+                                       cloneFixAttributes( srcElements[i], destElements[i] );
+                               }
+                       }
+               }
+
+               // Copy the events from the original to the clone
+               if ( dataAndEvents ) {
+                       cloneCopyEvent( elem, clone );
+
+                       if ( deepDataAndEvents ) {
+                               srcElements = getAll( elem );
+                               destElements = getAll( clone );
+
+                               for ( i = 0; srcElements[i]; ++i ) {
+                                       cloneCopyEvent( srcElements[i], destElements[i] );
+                               }
+                       }
+               }
+
+               srcElements = destElements = null;
+
+               // Return the cloned set
+               return clone;
+       },
+
+       clean: function( elems, context, fragment, scripts ) {
+               var j, safe, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
+                       i = 0,
+                       ret = [];
+
+               // Ensure that context is a document
+               if ( !context || typeof context.createDocumentFragment === "undefined" ) {
+                       context = document;
+               }
+
+               // Use the already-created safe fragment if context permits
+               for ( safe = context === document && safeFragment; (elem = elems[i]) != null; i++ ) {
+                       if ( typeof elem === "number" ) {
+                               elem += "";
+                       }
+
+                       if ( !elem ) {
+                               continue;
+                       }
+
+                       // Convert html string into DOM nodes
+                       if ( typeof elem === "string" ) {
+                               if ( !rhtml.test( elem ) ) {
+                                       elem = context.createTextNode( elem );
+                               } else {
+                                       // Ensure a safe container in which to render the html
+                                       safe = safe || createSafeFragment( context );
+                                       div = div || safe.appendChild( context.createElement("div") );
+
+                                       // Fix "XHTML"-style tags in all browsers
+                                       elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+                                       // Go to html and back, then peel off extra wrappers
+                                       tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+                                       wrap = wrapMap[ tag ] || wrapMap._default;
+                                       depth = wrap[0];
+                                       div.innerHTML = wrap[1] + elem + wrap[2];
+
+                                       // Move to the right depth
+                                       while ( depth-- ) {
+                                               div = div.lastChild;
+                                       }
+
+                                       // Remove IE's autoinserted <tbody> from table fragments
+                                       if ( !jQuery.support.tbody ) {
+
+                                               // String was a <table>, *may* have spurious <tbody>
+                                               hasBody = rtbody.test(elem);
+                                                       tbody = tag === "table" && !hasBody ?
+                                                               div.firstChild && div.firstChild.childNodes :
+
+                                                               // String was a bare <thead> or <tfoot>
+                                                               wrap[1] === "<table>" && !hasBody ?
+                                                                       div.childNodes :
+                                                                       [];
+
+                                               for ( j = tbody.length - 1; j >= 0 ; --j ) {
+                                                       if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+                                                               tbody[ j ].parentNode.removeChild( tbody[ j ] );
+                                                       }
+                                               }
+                                       }
+
+                                       // IE completely kills leading whitespace when innerHTML is used
+                                       if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+                                               div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+                                       }
+
+                                       elem = div.childNodes;
+
+                                       // Remember the top-level container for proper cleanup
+                                       div = safe.lastChild;
+                               }
+                       }
+
+                       if ( elem.nodeType ) {
+                               ret.push( elem );
+                       } else {
+                               ret = jQuery.merge( ret, elem );
+                       }
+               }
+
+               // Fix #11356: Clear elements from safeFragment
+               if ( div ) {
+                       safe.removeChild( div );
+                       elem = div = safe = null;
+               }
+
+               // Reset defaultChecked for any radios and checkboxes
+               // about to be appended to the DOM in IE 6/7 (#8060)
+               if ( !jQuery.support.appendChecked ) {
+                       for ( i = 0; (elem = ret[i]) != null; i++ ) {
+                               if ( jQuery.nodeName( elem, "input" ) ) {
+                                       fixDefaultChecked( elem );
+                               } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
+                                       jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+                               }
+                       }
+               }
+
+               // Append elements to a provided document fragment
+               if ( fragment ) {
+                       // Special handling of each script element
+                       handleScript = function( elem ) {
+                               // Check if we consider it executable
+                               if ( !elem.type || rscriptType.test( elem.type ) ) {
+                                       // Detach the script and store it in the scripts array (if provided) or the fragment
+                                       // Return truthy to indicate that it has been handled
+                                       return scripts ?
+                                               scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
+                                               fragment.appendChild( elem );
+                               }
+                       };
+
+                       for ( i = 0; (elem = ret[i]) != null; i++ ) {
+                               // Check if we're done after handling an executable script
+                               if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
+                                       // Append to fragment and handle embedded scripts
+                                       fragment.appendChild( elem );
+                                       if ( typeof elem.getElementsByTagName !== "undefined" ) {
+                                               // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+                                               jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
+
+                                               // Splice the scripts into ret after their former ancestor and advance our index beyond them
+                                               ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+                                               i += jsTags.length;
+                                       }
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       cleanData: function( elems, /* internal */ acceptData ) {
+               var data, id, elem, type,
+                       i = 0,
+                       internalKey = jQuery.expando,
+                       cache = jQuery.cache,
+                       deleteExpando = jQuery.support.deleteExpando,
+                       special = jQuery.event.special;
+
+               for ( ; (elem = elems[i]) != null; i++ ) {
+
+                       if ( acceptData || jQuery.acceptData( elem ) ) {
+
+                               id = elem[ internalKey ];
+                               data = id && cache[ id ];
+
+                               if ( data ) {
+                                       if ( data.events ) {
+                                               for ( type in data.events ) {
+                                                       if ( special[ type ] ) {
+                                                               jQuery.event.remove( elem, type );
+
+                                                       // This is a shortcut to avoid jQuery.event.remove's overhead
+                                                       } else {
+                                                               jQuery.removeEvent( elem, type, data.handle );
+                                                       }
+                                               }
+                                       }
+
+                                       // Remove cache only if it was not already removed by jQuery.event.remove
+                                       if ( cache[ id ] ) {
+
+                                               delete cache[ id ];
+
+                                               // IE does not allow us to delete expando properties from nodes,
+                                               // nor does it have a removeAttribute function on Document nodes;
+                                               // we must handle all of these cases
+                                               if ( deleteExpando ) {
+                                                       delete elem[ internalKey ];
+
+                                               } else if ( elem.removeAttribute ) {
+                                                       elem.removeAttribute( internalKey );
+
+                                               } else {
+                                                       elem[ internalKey ] = null;
+                                               }
+
+                                               jQuery.deletedIds.push( id );
+                                       }
+                               }
+                       }
+               }
+       }
+});
+// Limit scope pollution from any deprecated API
+(function() {
+
+var matched, browser;
+
+// Use of jQuery.browser is frowned upon.
+// More details: http://api.jquery.com/jQuery.browser
+// jQuery.uaMatch maintained for back-compat
+jQuery.uaMatch = function( ua ) {
+       ua = ua.toLowerCase();
+
+       var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+               /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+               /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+               /(msie) ([\w.]+)/.exec( ua ) ||
+               ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+               [];
+
+       return {
+               browser: match[ 1 ] || "",
+               version: match[ 2 ] || "0"
+       };
+};
+
+matched = jQuery.uaMatch( navigator.userAgent );
+browser = {};
+
+if ( matched.browser ) {
+       browser[ matched.browser ] = true;
+       browser.version = matched.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+// Maintained for back-compat only
+if ( browser.webkit ) {
+       browser.safari = true;
+}
+
+jQuery.browser = browser;
+
+jQuery.sub = function() {
+       function jQuerySub( selector, context ) {
+               return new jQuerySub.fn.init( selector, context );
+       }
+       jQuery.extend( true, jQuerySub, this );
+       jQuerySub.superclass = this;
+       jQuerySub.fn = jQuerySub.prototype = this();
+       jQuerySub.fn.constructor = jQuerySub;
+       jQuerySub.sub = this.sub;
+       jQuerySub.fn.init = function init( selector, context ) {
+               if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+                       context = jQuerySub( context );
+               }
+
+               return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+       };
+       jQuerySub.fn.init.prototype = jQuerySub.fn;
+       var rootjQuerySub = jQuerySub(document);
+       return jQuerySub;
+};
+       
+})();
+var curCSS, iframe, iframeDoc,
+       ralpha = /alpha\([^)]*\)/i,
+       ropacity = /opacity=([^)]*)/,
+       rposition = /^(top|right|bottom|left)$/,
+       rmargin = /^margin/,
+       rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+       rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+       rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
+       elemdisplay = {},
+
+       cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+       cssNormalTransform = {
+               letterSpacing: 0,
+               fontWeight: 400,
+               lineHeight: 1
+       },
+
+       cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+       cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
+
+       eventsToggle = jQuery.fn.toggle;
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+       // shortcut for names that are not vendor prefixed
+       if ( name in style ) {
+               return name;
+       }
+
+       // check for vendor prefixed names
+       var capName = name.charAt(0).toUpperCase() + name.slice(1),
+               origName = name,
+               i = cssPrefixes.length;
+
+       while ( i-- ) {
+               name = cssPrefixes[ i ] + capName;
+               if ( name in style ) {
+                       return name;
+               }
+       }
+
+       return origName;
+}
+
+function isHidden( elem, el ) {
+       elem = el || elem;
+       return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+       var elem, display,
+               values = [],
+               index = 0,
+               length = elements.length;
+
+       for ( ; index < length; index++ ) {
+               elem = elements[ index ];
+               if ( !elem.style ) {
+                       continue;
+               }
+               values[ index ] = jQuery._data( elem, "olddisplay" );
+               if ( show ) {
+                       // Reset the inline display of this element to learn if it is
+                       // being hidden by cascaded rules or not
+                       if ( !values[ index ] && elem.style.display === "none" ) {
+                               elem.style.display = "";
+                       }
+
+                       // Set elements which have been overridden with display: none
+                       // in a stylesheet to whatever the default browser style is
+                       // for such an element
+                       if ( elem.style.display === "" && isHidden( elem ) ) {
+                               values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+                       }
+               } else {
+                       display = curCSS( elem, "display" );
+
+                       if ( !values[ index ] && display !== "none" ) {
+                               jQuery._data( elem, "olddisplay", display );
+                       }
+               }
+       }
+
+       // Set the display of most of the elements in a second loop
+       // to avoid the constant reflow
+       for ( index = 0; index < length; index++ ) {
+               elem = elements[ index ];
+               if ( !elem.style ) {
+                       continue;
+               }
+               if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+                       elem.style.display = show ? values[ index ] || "" : "none";
+               }
+       }
+
+       return elements;
+}
+
+jQuery.fn.extend({
+       css: function( name, value ) {
+               return jQuery.access( this, function( elem, name, value ) {
+                       return value !== undefined ?
+                               jQuery.style( elem, name, value ) :
+                               jQuery.css( elem, name );
+               }, name, value, arguments.length > 1 );
+       },
+       show: function() {
+               return showHide( this, true );
+       },
+       hide: function() {
+               return showHide( this );
+       },
+       toggle: function( state, fn2 ) {
+               var bool = typeof state === "boolean";
+
+               if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
+                       return eventsToggle.apply( this, arguments );
+               }
+
+               return this.each(function() {
+                       if ( bool ? state : isHidden( this ) ) {
+                               jQuery( this ).show();
+                       } else {
+                               jQuery( this ).hide();
+                       }
+               });
+       }
+});
+
+jQuery.extend({
+       // Add in style property hooks for overriding the default
+       // behavior of getting and setting a style property
+       cssHooks: {
+               opacity: {
+                       get: function( elem, computed ) {
+                               if ( computed ) {
+                                       // We should always get a number back from opacity
+                                       var ret = curCSS( elem, "opacity" );
+                                       return ret === "" ? "1" : ret;
+
+                               }
+                       }
+               }
+       },
+
+       // Exclude the following css properties to add px
+       cssNumber: {
+               "fillOpacity": true,
+               "fontWeight": true,
+               "lineHeight": true,
+               "opacity": true,
+               "orphans": true,
+               "widows": true,
+               "zIndex": true,
+               "zoom": true
+       },
+
+       // Add in properties whose names you wish to fix before
+       // setting or getting the value
+       cssProps: {
+               // normalize float css property
+               "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+       },
+
+       // Get and set the style property on a DOM Node
+       style: function( elem, name, value, extra ) {
+               // Don't set styles on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+                       return;
+               }
+
+               // Make sure that we're working with the right name
+               var ret, type, hooks,
+                       origName = jQuery.camelCase( name ),
+                       style = elem.style;
+
+               name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+               // gets hook for the prefixed version
+               // followed by the unprefixed version
+               hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+               // Check if we're setting a value
+               if ( value !== undefined ) {
+                       type = typeof value;
+
+                       // convert relative number strings (+= or -=) to relative numbers. #7345
+                       if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+                               value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+                               // Fixes bug #9237
+                               type = "number";
+                       }
+
+                       // Make sure that NaN and null values aren't set. See: #7116
+                       if ( value == null || type === "number" && isNaN( value ) ) {
+                               return;
+                       }
+
+                       // If a number was passed in, add 'px' to the (except for certain CSS properties)
+                       if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+                               value += "px";
+                       }
+
+                       // If a hook was provided, use that value, otherwise just set the specified value
+                       if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+                               // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+                               // Fixes bug #5509
+                               try {
+                                       style[ name ] = value;
+                               } catch(e) {}
+                       }
+
+               } else {
+                       // If a hook was provided get the non-computed value from there
+                       if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+                               return ret;
+                       }
+
+                       // Otherwise just get the value from the style object
+                       return style[ name ];
+               }
+       },
+
+       css: function( elem, name, numeric, extra ) {
+               var val, num, hooks,
+                       origName = jQuery.camelCase( name );
+
+               // Make sure that we're working with the right name
+               name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+               // gets hook for the prefixed version
+               // followed by the unprefixed version
+               hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+               // If a hook was provided get the computed value from there
+               if ( hooks && "get" in hooks ) {
+                       val = hooks.get( elem, true, extra );
+               }
+
+               // Otherwise, if a way to get the computed value exists, use that
+               if ( val === undefined ) {
+                       val = curCSS( elem, name );
+               }
+
+               //convert "normal" to computed value
+               if ( val === "normal" && name in cssNormalTransform ) {
+                       val = cssNormalTransform[ name ];
+               }
+
+               // Return, converting to number if forced or a qualifier was provided and val looks numeric
+               if ( numeric || extra !== undefined ) {
+                       num = parseFloat( val );
+                       return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
+               }
+               return val;
+       },
+
+       // A method for quickly swapping in/out CSS properties to get correct calculations
+       swap: function( elem, options, callback ) {
+               var ret, name,
+                       old = {};
+
+               // Remember the old values, and insert the new ones
+               for ( name in options ) {
+                       old[ name ] = elem.style[ name ];
+                       elem.style[ name ] = options[ name ];
+               }
+
+               ret = callback.call( elem );
+
+               // Revert the old values
+               for ( name in options ) {
+                       elem.style[ name ] = old[ name ];
+               }
+
+               return ret;
+       }
+});
+
+// NOTE: To any future maintainer, we've used both window.getComputedStyle
+// and getComputedStyle here to produce a better gzip size
+if ( window.getComputedStyle ) {
+       curCSS = function( elem, name ) {
+               var ret, width, minWidth, maxWidth,
+                       computed = getComputedStyle( elem, null ),
+                       style = elem.style;
+
+               if ( computed ) {
+
+                       ret = computed[ name ];
+                       if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+                               ret = jQuery.style( elem, name );
+                       }
+
+                       // A tribute to the "awesome hack by Dean Edwards"
+                       // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+                       // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+                       // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+                       if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+                               width = style.width;
+                               minWidth = style.minWidth;
+                               maxWidth = style.maxWidth;
+
+                               style.minWidth = style.maxWidth = style.width = ret;
+                               ret = computed.width;
+
+                               style.width = width;
+                               style.minWidth = minWidth;
+                               style.maxWidth = maxWidth;
+                       }
+               }
+
+               return ret;
+       };
+} else if ( document.documentElement.currentStyle ) {
+       curCSS = function( elem, name ) {
+               var left, rsLeft,
+                       ret = elem.currentStyle && elem.currentStyle[ name ],
+                       style = elem.style;
+
+               // Avoid setting ret to empty string here
+               // so we don't default to auto
+               if ( ret == null && style && style[ name ] ) {
+                       ret = style[ name ];
+               }
+
+               // From the awesome hack by Dean Edwards
+               // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+               // If we're not dealing with a regular pixel number
+               // but a number that has a weird ending, we need to convert it to pixels
+               // but not position css attributes, as those are proportional to the parent element instead
+               // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+               if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+                       // Remember the original values
+                       left = style.left;
+                       rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+                       // Put in the new values to get a computed value out
+                       if ( rsLeft ) {
+                               elem.runtimeStyle.left = elem.currentStyle.left;
+                       }
+                       style.left = name === "fontSize" ? "1em" : ret;
+                       ret = style.pixelLeft + "px";
+
+                       // Revert the changed values
+                       style.left = left;
+                       if ( rsLeft ) {
+                               elem.runtimeStyle.left = rsLeft;
+                       }
+               }
+
+               return ret === "" ? "auto" : ret;
+       };
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+       var matches = rnumsplit.exec( value );
+       return matches ?
+                       Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+                       value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
+       var i = extra === ( isBorderBox ? "border" : "content" ) ?
+               // If we already have the right measurement, avoid augmentation
+               4 :
+               // Otherwise initialize for horizontal or vertical properties
+               name === "width" ? 1 : 0,
+
+               val = 0;
+
+       for ( ; i < 4; i += 2 ) {
+               // both box models exclude margin, so add it if we want it
+               if ( extra === "margin" ) {
+                       // we use jQuery.css instead of curCSS here
+                       // because of the reliableMarginRight CSS hook!
+                       val += jQuery.css( elem, extra + cssExpand[ i ], true );
+               }
+
+               // From this point on we use curCSS for maximum performance (relevant in animations)
+               if ( isBorderBox ) {
+                       // border-box includes padding, so remove it if we want content
+                       if ( extra === "content" ) {
+                               val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+                       }
+
+                       // at this point, extra isn't border nor margin, so remove border
+                       if ( extra !== "margin" ) {
+                               val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+                       }
+               } else {
+                       // at this point, extra isn't content, so add padding
+                       val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+
+                       // at this point, extra isn't content nor padding, so add border
+                       if ( extra !== "padding" ) {
+                               val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+                       }
+               }
+       }
+
+       return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+       // Start with offset property, which is equivalent to the border-box value
+       var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+               valueIsBorderBox = true,
+               isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
+
+       if ( val <= 0 ) {
+               // Fall back to computed then uncomputed css if necessary
+               val = curCSS( elem, name );
+               if ( val < 0 || val == null ) {
+                       val = elem.style[ name ];
+               }
+
+               // Computed unit is not pixels. Stop here and return.
+               if ( rnumnonpx.test(val) ) {
+                       return val;
+               }
+
+               // we need the check for style in case a browser which returns unreliable values
+               // for getComputedStyle silently falls back to the reliable elem.style
+               valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+               // Normalize "", auto, and prepare for extra
+               val = parseFloat( val ) || 0;
+       }
+
+       // use the active box-sizing model to add/subtract irrelevant styles
+       return ( val +
+               augmentWidthOrHeight(
+                       elem,
+                       name,
+                       extra || ( isBorderBox ? "border" : "content" ),
+                       valueIsBorderBox
+               )
+       ) + "px";
+}
+
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+       if ( elemdisplay[ nodeName ] ) {
+               return elemdisplay[ nodeName ];
+       }
+
+       var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
+               display = elem.css("display");
+       elem.remove();
+
+       // If the simple way fails,
+       // get element's real default display by attaching it to a temp iframe
+       if ( display === "none" || display === "" ) {
+               // Use the already-created iframe if possible
+               iframe = document.body.appendChild(
+                       iframe || jQuery.extend( document.createElement("iframe"), {
+                               frameBorder: 0,
+                               width: 0,
+                               height: 0
+                       })
+               );
+
+               // Create a cacheable copy of the iframe document on first call.
+               // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+               // document to it; WebKit & Firefox won't allow reusing the iframe document.
+               if ( !iframeDoc || !iframe.createElement ) {
+                       iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+                       iframeDoc.write("<!doctype html><html><body>");
+                       iframeDoc.close();
+               }
+
+               elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
+
+               display = curCSS( elem, "display" );
+               document.body.removeChild( iframe );
+       }
+
+       // Store the correct default display
+       elemdisplay[ nodeName ] = display;
+
+       return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+       jQuery.cssHooks[ name ] = {
+               get: function( elem, computed, extra ) {
+                       if ( computed ) {
+                               if ( elem.offsetWidth !== 0 || curCSS( elem, "display" ) !== "none" ) {
+                                       return getWidthOrHeight( elem, name, extra );
+                               } else {
+                                       return jQuery.swap( elem, cssShow, function() {
+                                               return getWidthOrHeight( elem, name, extra );
+                                       });
+                               }
+                       }
+               },
+
+               set: function( elem, value, extra ) {
+                       return setPositiveNumber( elem, value, extra ?
+                               augmentWidthOrHeight(
+                                       elem,
+                                       name,
+                                       extra,
+                                       jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
+                               ) : 0
+                       );
+               }
+       };
+});
+
+if ( !jQuery.support.opacity ) {
+       jQuery.cssHooks.opacity = {
+               get: function( elem, computed ) {
+                       // IE uses filters for opacity
+                       return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+                               ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+                               computed ? "1" : "";
+               },
+
+               set: function( elem, value ) {
+                       var style = elem.style,
+                               currentStyle = elem.currentStyle,
+                               opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+                               filter = currentStyle && currentStyle.filter || style.filter || "";
+
+                       // IE has trouble with opacity if it does not have layout
+                       // Force it by setting the zoom level
+                       style.zoom = 1;
+
+                       // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+                       if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+                               style.removeAttribute ) {
+
+                               // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+                               // if "filter:" is present at all, clearType is disabled, we want to avoid this
+                               // style.removeAttribute is IE Only, but so apparently is this code path...
+                               style.removeAttribute( "filter" );
+
+                               // if there there is no filter style applied in a css rule, we are done
+                               if ( currentStyle && !currentStyle.filter ) {
+                                       return;
+                               }
+                       }
+
+                       // otherwise, set new filter values
+                       style.filter = ralpha.test( filter ) ?
+                               filter.replace( ralpha, opacity ) :
+                               filter + " " + opacity;
+               }
+       };
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+       if ( !jQuery.support.reliableMarginRight ) {
+               jQuery.cssHooks.marginRight = {
+                       get: function( elem, computed ) {
+                               // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+                               // Work around by temporarily setting element display to inline-block
+                               return jQuery.swap( elem, { "display": "inline-block" }, function() {
+                                       if ( computed ) {
+                                               return curCSS( elem, "marginRight" );
+                                       }
+                               });
+                       }
+               };
+       }
+
+       // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+       // getComputedStyle returns percent when specified for top/left/bottom/right
+       // rather than make the css module depend on the offset module, we just check for it here
+       if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+               jQuery.each( [ "top", "left" ], function( i, prop ) {
+                       jQuery.cssHooks[ prop ] = {
+                               get: function( elem, computed ) {
+                                       if ( computed ) {
+                                               var ret = curCSS( elem, prop );
+                                               // if curCSS returns percentage, fallback to offset
+                                               return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
+                                       }
+                               }
+                       };
+               });
+       }
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.hidden = function( elem ) {
+               return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
+       };
+
+       jQuery.expr.filters.visible = function( elem ) {
+               return !jQuery.expr.filters.hidden( elem );
+       };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+       margin: "",
+       padding: "",
+       border: "Width"
+}, function( prefix, suffix ) {
+       jQuery.cssHooks[ prefix + suffix ] = {
+               expand: function( value ) {
+                       var i,
+
+                               // assumes a single number if not a string
+                               parts = typeof value === "string" ? value.split(" ") : [ value ],
+                               expanded = {};
+
+                       for ( i = 0; i < 4; i++ ) {
+                               expanded[ prefix + cssExpand[ i ] + suffix ] =
+                                       parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+                       }
+
+                       return expanded;
+               }
+       };
+
+       if ( !rmargin.test( prefix ) ) {
+               jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+       }
+});
+var r20 = /%20/g,
+       rbracket = /\[\]$/,
+       rCRLF = /\r?\n/g,
+       rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+       rselectTextarea = /^(?:select|textarea)/i;
+
+jQuery.fn.extend({
+       serialize: function() {
+               return jQuery.param( this.serializeArray() );
+       },
+       serializeArray: function() {
+               return this.map(function(){
+                       return this.elements ? jQuery.makeArray( this.elements ) : this;
+               })
+               .filter(function(){
+                       return this.name && !this.disabled &&
+                               ( this.checked || rselectTextarea.test( this.nodeName ) ||
+                                       rinput.test( this.type ) );
+               })
+               .map(function( i, elem ){
+                       var val = jQuery( this ).val();
+
+                       return val == null ?
+                               null :
+                               jQuery.isArray( val ) ?
+                                       jQuery.map( val, function( val, i ){
+                                               return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+                                       }) :
+                                       { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+               }).get();
+       }
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+       var prefix,
+               s = [],
+               add = function( key, value ) {
+                       // If value is a function, invoke it and return its value
+                       value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+                       s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+               };
+
+       // Set traditional to true for jQuery <= 1.3.2 behavior.
+       if ( traditional === undefined ) {
+               traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+       }
+
+       // If an array was passed in, assume that it is an array of form elements.
+       if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+               // Serialize the form elements
+               jQuery.each( a, function() {
+                       add( this.name, this.value );
+               });
+
+       } else {
+               // If traditional, encode the "old" way (the way 1.3.2 or older
+               // did it), otherwise encode params recursively.
+               for ( prefix in a ) {
+                       buildParams( prefix, a[ prefix ], traditional, add );
+               }
+       }
+
+       // Return the resulting serialization
+       return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+       var name;
+
+       if ( jQuery.isArray( obj ) ) {
+               // Serialize array item.
+               jQuery.each( obj, function( i, v ) {
+                       if ( traditional || rbracket.test( prefix ) ) {
+                               // Treat each array item as a scalar.
+                               add( prefix, v );
+
+                       } else {
+                               // If array item is non-scalar (array or object), encode its
+                               // numeric index to resolve deserialization ambiguity issues.
+                               // Note that rack (as of 1.0.0) can't currently deserialize
+                               // nested arrays properly, and attempting to do so may cause
+                               // a server error. Possible fixes are to modify rack's
+                               // deserialization algorithm or to provide an option or flag
+                               // to force array serialization to be shallow.
+                               buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+                       }
+               });
+
+       } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+               // Serialize object item.
+               for ( name in obj ) {
+                       buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+               }
+
+       } else {
+               // Serialize scalar item.
+               add( prefix, obj );
+       }
+}
+var // Document location
+       ajaxLocation,
+       // Document location segments
+       ajaxLocParts,
+
+       rhash = /#.*$/,
+       rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+       // #7653, #8125, #8152: local protocol detection
+       rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+       rnoContent = /^(?:GET|HEAD)$/,
+       rprotocol = /^\/\//,
+       rquery = /\?/,
+       rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+       rts = /([?&])_=[^&]*/,
+       rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+       // Keep a copy of the old load method
+       _load = jQuery.fn.load,
+
+       /* Prefilters
+        * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+        * 2) These are called:
+        *    - BEFORE asking for a transport
+        *    - AFTER param serialization (s.data is a string if s.processData is true)
+        * 3) key is the dataType
+        * 4) the catchall symbol "*" can be used
+        * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+        */
+       prefilters = {},
+
+       /* Transports bindings
+        * 1) key is the dataType
+        * 2) the catchall symbol "*" can be used
+        * 3) selection will start with transport dataType and THEN go to "*" if needed
+        */
+       transports = {},
+
+       // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+       allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+       ajaxLocation = location.href;
+} catch( e ) {
+       // Use the href attribute of an A element
+       // since IE will modify it given document.location
+       ajaxLocation = document.createElement( "a" );
+       ajaxLocation.href = "";
+       ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+       // dataTypeExpression is optional and defaults to "*"
+       return function( dataTypeExpression, func ) {
+
+               if ( typeof dataTypeExpression !== "string" ) {
+                       func = dataTypeExpression;
+                       dataTypeExpression = "*";
+               }
+
+               var dataType, list, placeBefore,
+                       dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ),
+                       i = 0,
+                       length = dataTypes.length;
+
+               if ( jQuery.isFunction( func ) ) {
+                       // For each dataType in the dataTypeExpression
+                       for ( ; i < length; i++ ) {
+                               dataType = dataTypes[ i ];
+                               // We control if we're asked to add before
+                               // any existing element
+                               placeBefore = /^\+/.test( dataType );
+                               if ( placeBefore ) {
+                                       dataType = dataType.substr( 1 ) || "*";
+                               }
+                               list = structure[ dataType ] = structure[ dataType ] || [];
+                               // then we add to the structure accordingly
+                               list[ placeBefore ? "unshift" : "push" ]( func );
+                       }
+               }
+       };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+               dataType /* internal */, inspected /* internal */ ) {
+
+       dataType = dataType || options.dataTypes[ 0 ];
+       inspected = inspected || {};
+
+       inspected[ dataType ] = true;
+
+       var selection,
+               list = structure[ dataType ],
+               i = 0,
+               length = list ? list.length : 0,
+               executeOnly = ( structure === prefilters );
+
+       for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+               selection = list[ i ]( options, originalOptions, jqXHR );
+               // If we got redirected to another dataType
+               // we try there if executing only and not done already
+               if ( typeof selection === "string" ) {
+                       if ( !executeOnly || inspected[ selection ] ) {
+                               selection = undefined;
+                       } else {
+                               options.dataTypes.unshift( selection );
+                               selection = inspectPrefiltersOrTransports(
+                                               structure, options, originalOptions, jqXHR, selection, inspected );
+                       }
+               }
+       }
+       // If we're only executing or nothing was selected
+       // we try the catchall dataType if not done already
+       if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+               selection = inspectPrefiltersOrTransports(
+                               structure, options, originalOptions, jqXHR, "*", inspected );
+       }
+       // unnecessary when only executing (prefilters)
+       // but it'll be ignored by the caller in that case
+       return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+       var key, deep,
+               flatOptions = jQuery.ajaxSettings.flatOptions || {};
+       for ( key in src ) {
+               if ( src[ key ] !== undefined ) {
+                       ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+               }
+       }
+       if ( deep ) {
+               jQuery.extend( true, target, deep );
+       }
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+       if ( typeof url !== "string" && _load ) {
+               return _load.apply( this, arguments );
+       }
+
+       // Don't do a request if no elements are being requested
+       if ( !this.length ) {
+               return this;
+       }
+
+       var selector, type, response,
+               self = this,
+               off = url.indexOf(" ");
+
+       if ( off >= 0 ) {
+               selector = url.slice( off, url.length );
+               url = url.slice( 0, off );
+       }
+
+       // If it's a function
+       if ( jQuery.isFunction( params ) ) {
+
+               // We assume that it's the callback
+               callback = params;
+               params = undefined;
+
+       // Otherwise, build a param string
+       } else if ( typeof params === "object" ) {
+               type = "POST";
+       }
+
+       // Request the remote document
+       jQuery.ajax({
+               url: url,
+
+               // if "type" variable is undefined, then "GET" method will be used
+               type: type,
+               dataType: "html",
+               data: params,
+               complete: function( jqXHR, status ) {
+                       if ( callback ) {
+                               self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+                       }
+               }
+       }).done(function( responseText ) {
+
+               // Save response for use in complete callback
+               response = arguments;
+
+               // See if a selector was specified
+               self.html( selector ?
+
+                       // Create a dummy div to hold the results
+                       jQuery("<div>")
+
+                               // inject the contents of the document in, removing the scripts
+                               // to avoid any 'Permission Denied' errors in IE
+                               .append( responseText.replace( rscript, "" ) )
+
+                               // Locate the specified elements
+                               .find( selector ) :
+
+                       // If not, just inject the full result
+                       responseText );
+
+       });
+
+       return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+       jQuery.fn[ o ] = function( f ){
+               return this.on( o, f );
+       };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+       jQuery[ method ] = function( url, data, callback, type ) {
+               // shift arguments if data argument was omitted
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = undefined;
+               }
+
+               return jQuery.ajax({
+                       type: method,
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       };
+});
+
+jQuery.extend({
+
+       getScript: function( url, callback ) {
+               return jQuery.get( url, undefined, callback, "script" );
+       },
+
+       getJSON: function( url, data, callback ) {
+               return jQuery.get( url, data, callback, "json" );
+       },
+
+       // Creates a full fledged settings object into target
+       // with both ajaxSettings and settings fields.
+       // If target is omitted, writes into ajaxSettings.
+       ajaxSetup: function( target, settings ) {
+               if ( settings ) {
+                       // Building a settings object
+                       ajaxExtend( target, jQuery.ajaxSettings );
+               } else {
+                       // Extending ajaxSettings
+                       settings = target;
+                       target = jQuery.ajaxSettings;
+               }
+               ajaxExtend( target, settings );
+               return target;
+       },
+
+       ajaxSettings: {
+               url: ajaxLocation,
+               isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+               global: true,
+               type: "GET",
+               contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+               processData: true,
+               async: true,
+               /*
+               timeout: 0,
+               data: null,
+               dataType: null,
+               username: null,
+               password: null,
+               cache: null,
+               throws: false,
+               traditional: false,
+               headers: {},
+               */
+
+               accepts: {
+                       xml: "application/xml, text/xml",
+                       html: "text/html",
+                       text: "text/plain",
+                       json: "application/json, text/javascript",
+                       "*": allTypes
+               },
+
+               contents: {
+                       xml: /xml/,
+                       html: /html/,
+                       json: /json/
+               },
+
+               responseFields: {
+                       xml: "responseXML",
+                       text: "responseText"
+               },
+
+               // List of data converters
+               // 1) key format is "source_type destination_type" (a single space in-between)
+               // 2) the catchall symbol "*" can be used for source_type
+               converters: {
+
+                       // Convert anything to text
+                       "* text": window.String,
+
+                       // Text to html (true = no transformation)
+                       "text html": true,
+
+                       // Evaluate text as a json expression
+                       "text json": jQuery.parseJSON,
+
+                       // Parse text as xml
+                       "text xml": jQuery.parseXML
+               },
+
+               // For options that shouldn't be deep extended:
+               // you can add your own custom options here if
+               // and when you create one that shouldn't be
+               // deep extended (see ajaxExtend)
+               flatOptions: {
+                       context: true,
+                       url: true
+               }
+       },
+
+       ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+       ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+       // Main method
+       ajax: function( url, options ) {
+
+               // If url is an object, simulate pre-1.5 signature
+               if ( typeof url === "object" ) {
+                       options = url;
+                       url = undefined;
+               }
+
+               // Force options to be an object
+               options = options || {};
+
+               var // ifModified key
+                       ifModifiedKey,
+                       // Response headers
+                       responseHeadersString,
+                       responseHeaders,
+                       // transport
+                       transport,
+                       // timeout handle
+                       timeoutTimer,
+                       // Cross-domain detection vars
+                       parts,
+                       // To know if global events are to be dispatched
+                       fireGlobals,
+                       // Loop variable
+                       i,
+                       // Create the final options object
+                       s = jQuery.ajaxSetup( {}, options ),
+                       // Callbacks context
+                       callbackContext = s.context || s,
+                       // Context for global events
+                       // It's the callbackContext if one was provided in the options
+                       // and if it's a DOM node or a jQuery collection
+                       globalEventContext = callbackContext !== s &&
+                               ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+                                               jQuery( callbackContext ) : jQuery.event,
+                       // Deferreds
+                       deferred = jQuery.Deferred(),
+                       completeDeferred = jQuery.Callbacks( "once memory" ),
+                       // Status-dependent callbacks
+                       statusCode = s.statusCode || {},
+                       // Headers (they are sent all at once)
+                       requestHeaders = {},
+                       requestHeadersNames = {},
+                       // The jqXHR state
+                       state = 0,
+                       // Default abort message
+                       strAbort = "canceled",
+                       // Fake xhr
+                       jqXHR = {
+
+                               readyState: 0,
+
+                               // Caches the header
+                               setRequestHeader: function( name, value ) {
+                                       if ( !state ) {
+                                               var lname = name.toLowerCase();
+                                               name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+                                               requestHeaders[ name ] = value;
+                                       }
+                                       return this;
+                               },
+
+                               // Raw string
+                               getAllResponseHeaders: function() {
+                                       return state === 2 ? responseHeadersString : null;
+                               },
+
+                               // Builds headers hashtable if needed
+                               getResponseHeader: function( key ) {
+                                       var match;
+                                       if ( state === 2 ) {
+                                               if ( !responseHeaders ) {
+                                                       responseHeaders = {};
+                                                       while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+                                                               responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+                                                       }
+                                               }
+                                               match = responseHeaders[ key.toLowerCase() ];
+                                       }
+                                       return match === undefined ? null : match;
+                               },
+
+                               // Overrides response content-type header
+                               overrideMimeType: function( type ) {
+                                       if ( !state ) {
+                                               s.mimeType = type;
+                                       }
+                                       return this;
+                               },
+
+                               // Cancel the request
+                               abort: function( statusText ) {
+                                       statusText = statusText || strAbort;
+                                       if ( transport ) {
+                                               transport.abort( statusText );
+                                       }
+                                       done( 0, statusText );
+                                       return this;
+                               }
+                       };
+
+               // Callback for when everything is done
+               // It is defined here because jslint complains if it is declared
+               // at the end of the function (which would be more logical and readable)
+               function done( status, nativeStatusText, responses, headers ) {
+                       var isSuccess, success, error, response, modified,
+                               statusText = nativeStatusText;
+
+                       // Called once
+                       if ( state === 2 ) {
+                               return;
+                       }
+
+                       // State is "done" now
+                       state = 2;
+
+                       // Clear timeout if it exists
+                       if ( timeoutTimer ) {
+                               clearTimeout( timeoutTimer );
+                       }
+
+                       // Dereference transport for early garbage collection
+                       // (no matter how long the jqXHR object will be used)
+                       transport = undefined;
+
+                       // Cache response headers
+                       responseHeadersString = headers || "";
+
+                       // Set readyState
+                       jqXHR.readyState = status > 0 ? 4 : 0;
+
+                       // Get response data
+                       if ( responses ) {
+                               response = ajaxHandleResponses( s, jqXHR, responses );
+                       }
+
+                       // If successful, handle type chaining
+                       if ( status >= 200 && status < 300 || status === 304 ) {
+
+                               // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+                               if ( s.ifModified ) {
+
+                                       modified = jqXHR.getResponseHeader("Last-Modified");
+                                       if ( modified ) {
+                                               jQuery.lastModified[ ifModifiedKey ] = modified;
+                                       }
+                                       modified = jqXHR.getResponseHeader("Etag");
+                                       if ( modified ) {
+                                               jQuery.etag[ ifModifiedKey ] = modified;
+                                       }
+                               }
+
+                               // If not modified
+                               if ( status === 304 ) {
+
+                                       statusText = "notmodified";
+                                       isSuccess = true;
+
+                               // If we have data
+                               } else {
+
+                                       isSuccess = ajaxConvert( s, response );
+                                       statusText = isSuccess.state;
+                                       success = isSuccess.data;
+                                       error = isSuccess.error;
+                                       isSuccess = !error;
+                               }
+                       } else {
+                               // We extract error from statusText
+                               // then normalize statusText and status for non-aborts
+                               error = statusText;
+                               if ( !statusText || status ) {
+                                       statusText = "error";
+                                       if ( status < 0 ) {
+                                               status = 0;
+                                       }
+                               }
+                       }
+
+                       // Set data for the fake xhr object
+                       jqXHR.status = status;
+                       jqXHR.statusText = "" + ( nativeStatusText || statusText );
+
+                       // Success/Error
+                       if ( isSuccess ) {
+                               deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+                       } else {
+                               deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+                       }
+
+                       // Status-dependent callbacks
+                       jqXHR.statusCode( statusCode );
+                       statusCode = undefined;
+
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+                                               [ jqXHR, s, isSuccess ? success : error ] );
+                       }
+
+                       // Complete
+                       completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+                               // Handle the global AJAX counter
+                               if ( !( --jQuery.active ) ) {
+                                       jQuery.event.trigger( "ajaxStop" );
+                               }
+                       }
+               }
+
+               // Attach deferreds
+               deferred.promise( jqXHR );
+               jqXHR.success = jqXHR.done;
+               jqXHR.error = jqXHR.fail;
+               jqXHR.complete = completeDeferred.add;
+
+               // Status-dependent callbacks
+               jqXHR.statusCode = function( map ) {
+                       if ( map ) {
+                               var tmp;
+                               if ( state < 2 ) {
+                                       for ( tmp in map ) {
+                                               statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+                                       }
+                               } else {
+                                       tmp = map[ jqXHR.status ];
+                                       jqXHR.always( tmp );
+                               }
+                       }
+                       return this;
+               };
+
+               // Remove hash character (#7531: and string promotion)
+               // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+               // We also use the url parameter if available
+               s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+               // Extract dataTypes list
+               s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace );
+
+               // Determine if a cross-domain request is in order
+               if ( s.crossDomain == null ) {
+                       parts = rurl.exec( s.url.toLowerCase() );
+                       s.crossDomain = !!( parts &&
+                               ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+                                       ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+                                               ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+                       );
+               }
+
+               // Convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" ) {
+                       s.data = jQuery.param( s.data, s.traditional );
+               }
+
+               // Apply prefilters
+               inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+               // If request was aborted inside a prefilter, stop there
+               if ( state === 2 ) {
+                       return jqXHR;
+               }
+
+               // We can fire global events as of now if asked to
+               fireGlobals = s.global;
+
+               // Uppercase the type
+               s.type = s.type.toUpperCase();
+
+               // Determine if request has content
+               s.hasContent = !rnoContent.test( s.type );
+
+               // Watch for a new set of requests
+               if ( fireGlobals && jQuery.active++ === 0 ) {
+                       jQuery.event.trigger( "ajaxStart" );
+               }
+
+               // More options handling for requests with no content
+               if ( !s.hasContent ) {
+
+                       // If data is available, append data to url
+                       if ( s.data ) {
+                               s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+                               // #9682: remove data so that it's not used in an eventual retry
+                               delete s.data;
+                       }
+
+                       // Get ifModifiedKey before adding the anti-cache parameter
+                       ifModifiedKey = s.url;
+
+                       // Add anti-cache in url if needed
+                       if ( s.cache === false ) {
+
+                               var ts = jQuery.now(),
+                                       // try replacing _= if it is there
+                                       ret = s.url.replace( rts, "$1_=" + ts );
+
+                               // if nothing was replaced, add timestamp to the end
+                               s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+                       }
+               }
+
+               // Set the correct header, if data is being sent
+               if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+                       jqXHR.setRequestHeader( "Content-Type", s.contentType );
+               }
+
+               // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+               if ( s.ifModified ) {
+                       ifModifiedKey = ifModifiedKey || s.url;
+                       if ( jQuery.lastModified[ ifModifiedKey ] ) {
+                               jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+                       }
+                       if ( jQuery.etag[ ifModifiedKey ] ) {
+                               jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+                       }
+               }
+
+               // Set the Accepts header for the server, depending on the dataType
+               jqXHR.setRequestHeader(
+                       "Accept",
+                       s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+                               s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+                               s.accepts[ "*" ]
+               );
+
+               // Check for headers option
+               for ( i in s.headers ) {
+                       jqXHR.setRequestHeader( i, s.headers[ i ] );
+               }
+
+               // Allow custom headers/mimetypes and early abort
+               if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+                               // Abort if not done already and return
+                               return jqXHR.abort();
+
+               }
+
+               // aborting is no longer a cancellation
+               strAbort = "abort";
+
+               // Install callbacks on deferreds
+               for ( i in { success: 1, error: 1, complete: 1 } ) {
+                       jqXHR[ i ]( s[ i ] );
+               }
+
+               // Get transport
+               transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+               // If no transport, we auto-abort
+               if ( !transport ) {
+                       done( -1, "No Transport" );
+               } else {
+                       jqXHR.readyState = 1;
+                       // Send global event
+                       if ( fireGlobals ) {
+                               globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+                       }
+                       // Timeout
+                       if ( s.async && s.timeout > 0 ) {
+                               timeoutTimer = setTimeout( function(){
+                                       jqXHR.abort( "timeout" );
+                               }, s.timeout );
+                       }
+
+                       try {
+                               state = 1;
+                               transport.send( requestHeaders, done );
+                       } catch (e) {
+                               // Propagate exception as error if not done
+                               if ( state < 2 ) {
+                                       done( -1, e );
+                               // Simply rethrow otherwise
+                               } else {
+                                       throw e;
+                               }
+                       }
+               }
+
+               return jqXHR;
+       },
+
+       // Counter for holding the number of active queries
+       active: 0,
+
+       // Last-Modified header cache for next request
+       lastModified: {},
+       etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+       var ct, type, finalDataType, firstDataType,
+               contents = s.contents,
+               dataTypes = s.dataTypes,
+               responseFields = s.responseFields;
+
+       // Fill responseXXX fields
+       for ( type in responseFields ) {
+               if ( type in responses ) {
+                       jqXHR[ responseFields[type] ] = responses[ type ];
+               }
+       }
+
+       // Remove auto dataType and get content-type in the process
+       while( dataTypes[ 0 ] === "*" ) {
+               dataTypes.shift();
+               if ( ct === undefined ) {
+                       ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+               }
+       }
+
+       // Check if we're dealing with a known content-type
+       if ( ct ) {
+               for ( type in contents ) {
+                       if ( contents[ type ] && contents[ type ].test( ct ) ) {
+                               dataTypes.unshift( type );
+                               break;
+                       }
+               }
+       }
+
+       // Check to see if we have a response for the expected dataType
+       if ( dataTypes[ 0 ] in responses ) {
+               finalDataType = dataTypes[ 0 ];
+       } else {
+               // Try convertible dataTypes
+               for ( type in responses ) {
+                       if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+                               finalDataType = type;
+                               break;
+                       }
+                       if ( !firstDataType ) {
+                               firstDataType = type;
+                       }
+               }
+               // Or just use first one
+               finalDataType = finalDataType || firstDataType;
+       }
+
+       // If we found a dataType
+       // We add the dataType to the list if needed
+       // and return the corresponding response
+       if ( finalDataType ) {
+               if ( finalDataType !== dataTypes[ 0 ] ) {
+                       dataTypes.unshift( finalDataType );
+               }
+               return responses[ finalDataType ];
+       }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+       var conv, conv2, current, tmp,
+               // Work with a copy of dataTypes in case we need to modify it for conversion
+               dataTypes = s.dataTypes.slice(),
+               prev = dataTypes[ 0 ],
+               converters = {},
+               i = 0;
+
+       // Apply the dataFilter if provided
+       if ( s.dataFilter ) {
+               response = s.dataFilter( response, s.dataType );
+       }
+
+       // Create converters map with lowercased keys
+       if ( dataTypes[ 1 ] ) {
+               for ( conv in s.converters ) {
+                       converters[ conv.toLowerCase() ] = s.converters[ conv ];
+               }
+       }
+
+       // Convert to each sequential dataType, tolerating list modification
+       for ( ; (current = dataTypes[++i]); ) {
+
+               // There's only work to do if current dataType is non-auto
+               if ( current !== "*" ) {
+
+                       // Convert response if prev dataType is non-auto and differs from current
+                       if ( prev !== "*" && prev !== current ) {
+
+                               // Seek a direct converter
+                               conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+                               // If none found, seek a pair
+                               if ( !conv ) {
+                                       for ( conv2 in converters ) {
+
+                                               // If conv2 outputs current
+                                               tmp = conv2.split(" ");
+                                               if ( tmp[ 1 ] === current ) {
+
+                                                       // If prev can be converted to accepted input
+                                                       conv = converters[ prev + " " + tmp[ 0 ] ] ||
+                                                               converters[ "* " + tmp[ 0 ] ];
+                                                       if ( conv ) {
+                                                               // Condense equivalence converters
+                                                               if ( conv === true ) {
+                                                                       conv = converters[ conv2 ];
+
+                                                               // Otherwise, insert the intermediate dataType
+                                                               } else if ( converters[ conv2 ] !== true ) {
+                                                                       current = tmp[ 0 ];
+                                                                       dataTypes.splice( i--, 0, current );
+                                                               }
+
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               // Apply converter (if not an equivalence)
+                               if ( conv !== true ) {
+
+                                       // Unless errors are allowed to bubble, catch and return them
+                                       if ( conv && s["throws"] ) {
+                                               response = conv( response );
+                                       } else {
+                                               try {
+                                                       response = conv( response );
+                                               } catch ( e ) {
+                                                       return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+                                               }
+                                       }
+                               }
+                       }
+
+                       // Update prev for next iteration
+                       prev = current;
+               }
+       }
+
+       return { state: "success", data: response };
+}
+var oldCallbacks = [],
+       rquestion = /\?/,
+       rjsonp = /(=)\?(?=&|$)|\?\?/,
+       nonce = jQuery.now();
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+       jsonp: "callback",
+       jsonpCallback: function() {
+               var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+               this[ callback ] = true;
+               return callback;
+       }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+       var callbackName, overwritten, responseContainer,
+               data = s.data,
+               url = s.url,
+               hasCallback = s.jsonp !== false,
+               replaceInUrl = hasCallback && rjsonp.test( url ),
+               replaceInData = hasCallback && !replaceInUrl && typeof data === "string" &&
+                       !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") &&
+                       rjsonp.test( data );
+
+       // Handle iff the expected data type is "jsonp" or we have a parameter to set
+       if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) {
+
+               // Get callback name, remembering preexisting value associated with it
+               callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+                       s.jsonpCallback() :
+                       s.jsonpCallback;
+               overwritten = window[ callbackName ];
+
+               // Insert callback into url or form data
+               if ( replaceInUrl ) {
+                       s.url = url.replace( rjsonp, "$1" + callbackName );
+               } else if ( replaceInData ) {
+                       s.data = data.replace( rjsonp, "$1" + callbackName );
+               } else if ( hasCallback ) {
+                       s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+               }
+
+               // Use data converter to retrieve json after script execution
+               s.converters["script json"] = function() {
+                       if ( !responseContainer ) {
+                               jQuery.error( callbackName + " was not called" );
+                       }
+                       return responseContainer[ 0 ];
+               };
+
+               // force json dataType
+               s.dataTypes[ 0 ] = "json";
+
+               // Install callback
+               window[ callbackName ] = function() {
+                       responseContainer = arguments;
+               };
+
+               // Clean-up function (fires after converters)
+               jqXHR.always(function() {
+                       // Restore preexisting value
+                       window[ callbackName ] = overwritten;
+
+                       // Save back as free
+                       if ( s[ callbackName ] ) {
+                               // make sure that re-using the options doesn't screw things around
+                               s.jsonpCallback = originalSettings.jsonpCallback;
+
+                               // save the callback name for future use
+                               oldCallbacks.push( callbackName );
+                       }
+
+                       // Call if it was a function and we have a response
+                       if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+                               overwritten( responseContainer[ 0 ] );
+                       }
+
+                       responseContainer = overwritten = undefined;
+               });
+
+               // Delegate to script
+               return "script";
+       }
+});
+// Install script dataType
+jQuery.ajaxSetup({
+       accepts: {
+               script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+       },
+       contents: {
+               script: /javascript|ecmascript/
+       },
+       converters: {
+               "text script": function( text ) {
+                       jQuery.globalEval( text );
+                       return text;
+               }
+       }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+       if ( s.cache === undefined ) {
+               s.cache = false;
+       }
+       if ( s.crossDomain ) {
+               s.type = "GET";
+               s.global = false;
+       }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+       // This transport only deals with cross domain requests
+       if ( s.crossDomain ) {
+
+               var script,
+                       head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+               return {
+
+                       send: function( _, callback ) {
+
+                               script = document.createElement( "script" );
+
+                               script.async = "async";
+
+                               if ( s.scriptCharset ) {
+                                       script.charset = s.scriptCharset;
+                               }
+
+                               script.src = s.url;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+                                       if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+
+                                               // Remove the script
+                                               if ( head && script.parentNode ) {
+                                                       head.removeChild( script );
+                                               }
+
+                                               // Dereference the script
+                                               script = undefined;
+
+                                               // Callback if not abort
+                                               if ( !isAbort ) {
+                                                       callback( 200, "success" );
+                                               }
+                                       }
+                               };
+                               // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+                               // This arises when a base node is used (#2709 and #4378).
+                               head.insertBefore( script, head.firstChild );
+                       },
+
+                       abort: function() {
+                               if ( script ) {
+                                       script.onload( 0, 1 );
+                               }
+                       }
+               };
+       }
+});
+var xhrCallbacks,
+       // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+       xhrOnUnloadAbort = window.ActiveXObject ? function() {
+               // Abort all pending requests
+               for ( var key in xhrCallbacks ) {
+                       xhrCallbacks[ key ]( 0, 1 );
+               }
+       } : false,
+       xhrId = 0;
+
+// Functions to create xhrs
+function createStandardXHR() {
+       try {
+               return new window.XMLHttpRequest();
+       } catch( e ) {}
+}
+
+function createActiveXHR() {
+       try {
+               return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+       } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+       /* Microsoft failed to properly
+        * implement the XMLHttpRequest in IE7 (can't request local files),
+        * so we use the ActiveXObject when it is available
+        * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+        * we need a fallback.
+        */
+       function() {
+               return !this.isLocal && createStandardXHR() || createActiveXHR();
+       } :
+       // For all other browsers, use the standard XMLHttpRequest object
+       createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+       jQuery.extend( jQuery.support, {
+               ajax: !!xhr,
+               cors: !!xhr && ( "withCredentials" in xhr )
+       });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+       jQuery.ajaxTransport(function( s ) {
+               // Cross domain only allowed if supported through XMLHttpRequest
+               if ( !s.crossDomain || jQuery.support.cors ) {
+
+                       var callback;
+
+                       return {
+                               send: function( headers, complete ) {
+
+                                       // Get a new xhr
+                                       var handle, i,
+                                               xhr = s.xhr();
+
+                                       // Open the socket
+                                       // Passing null username, generates a login popup on Opera (#2865)
+                                       if ( s.username ) {
+                                               xhr.open( s.type, s.url, s.async, s.username, s.password );
+                                       } else {
+                                               xhr.open( s.type, s.url, s.async );
+                                       }
+
+                                       // Apply custom fields if provided
+                                       if ( s.xhrFields ) {
+                                               for ( i in s.xhrFields ) {
+                                                       xhr[ i ] = s.xhrFields[ i ];
+                                               }
+                                       }
+
+                                       // Override mime type if needed
+                                       if ( s.mimeType && xhr.overrideMimeType ) {
+                                               xhr.overrideMimeType( s.mimeType );
+                                       }
+
+                                       // X-Requested-With header
+                                       // For cross-domain requests, seeing as conditions for a preflight are
+                                       // akin to a jigsaw puzzle, we simply never set it to be sure.
+                                       // (it can always be set on a per-request basis or even using ajaxSetup)
+                                       // For same-domain requests, won't change header if already provided.
+                                       if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+                                               headers[ "X-Requested-With" ] = "XMLHttpRequest";
+                                       }
+
+                                       // Need an extra try/catch for cross domain requests in Firefox 3
+                                       try {
+                                               for ( i in headers ) {
+                                                       xhr.setRequestHeader( i, headers[ i ] );
+                                               }
+                                       } catch( _ ) {}
+
+                                       // Do send the request
+                                       // This may raise an exception which is actually
+                                       // handled in jQuery.ajax (so no try/catch here)
+                                       xhr.send( ( s.hasContent && s.data ) || null );
+
+                                       // Listener
+                                       callback = function( _, isAbort ) {
+
+                                               var status,
+                                                       statusText,
+                                                       responseHeaders,
+                                                       responses,
+                                                       xml;
+
+                                               // Firefox throws exceptions when accessing properties
+                                               // of an xhr when a network error occurred
+                                               // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+                                               try {
+
+                                                       // Was never called and is aborted or complete
+                                                       if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+                                                               // Only called once
+                                                               callback = undefined;
+
+                                                               // Do not keep as active anymore
+                                                               if ( handle ) {
+                                                                       xhr.onreadystatechange = jQuery.noop;
+                                                                       if ( xhrOnUnloadAbort ) {
+                                                                               delete xhrCallbacks[ handle ];
+                                                                       }
+                                                               }
+
+                                                               // If it's an abort
+                                                               if ( isAbort ) {
+                                                                       // Abort it manually if needed
+                                                                       if ( xhr.readyState !== 4 ) {
+                                                                               xhr.abort();
+                                                                       }
+                                                               } else {
+                                                                       status = xhr.status;
+                                                                       responseHeaders = xhr.getAllResponseHeaders();
+                                                                       responses = {};
+                                                                       xml = xhr.responseXML;
+
+                                                                       // Construct response list
+                                                                       if ( xml && xml.documentElement /* #4958 */ ) {
+                                                                               responses.xml = xml;
+                                                                       }
+
+                                                                       // When requesting binary data, IE6-9 will throw an exception
+                                                                       // on any attempt to access responseText (#11426)
+                                                                       try {
+                                                                               responses.text = xhr.responseText;
+                                                                       } catch( _ ) {
+                                                                       }
+
+                                                                       // Firefox throws an exception when accessing
+                                                                       // statusText for faulty cross-domain requests
+                                                                       try {
+                                                                               statusText = xhr.statusText;
+                                                                       } catch( e ) {
+                                                                               // We normalize with Webkit giving an empty statusText
+                                                                               statusText = "";
+                                                                       }
+
+                                                                       // Filter status for non standard behaviors
+
+                                                                       // If the request is local and we have data: assume a success
+                                                                       // (success with no data won't get notified, that's the best we
+                                                                       // can do given current implementations)
+                                                                       if ( !status && s.isLocal && !s.crossDomain ) {
+                                                                               status = responses.text ? 200 : 404;
+                                                                       // IE - #1450: sometimes returns 1223 when it should be 204
+                                                                       } else if ( status === 1223 ) {
+                                                                               status = 204;
+                                                                       }
+                                                               }
+                                                       }
+                                               } catch( firefoxAccessException ) {
+                                                       if ( !isAbort ) {
+                                                               complete( -1, firefoxAccessException );
+                                                       }
+                                               }
+
+                                               // Call complete if needed
+                                               if ( responses ) {
+                                                       complete( status, statusText, responses, responseHeaders );
+                                               }
+                                       };
+
+                                       if ( !s.async ) {
+                                               // if we're in sync mode we fire the callback
+                                               callback();
+                                       } else if ( xhr.readyState === 4 ) {
+                                               // (IE6 & IE7) if it's in cache and has been
+                                               // retrieved directly we need to fire the callback
+                                               setTimeout( callback, 0 );
+                                       } else {
+                                               handle = ++xhrId;
+                                               if ( xhrOnUnloadAbort ) {
+                                                       // Create the active xhrs callbacks list if needed
+                                                       // and attach the unload handler
+                                                       if ( !xhrCallbacks ) {
+                                                               xhrCallbacks = {};
+                                                               jQuery( window ).unload( xhrOnUnloadAbort );
+                                                       }
+                                                       // Add to list of active xhrs callbacks
+                                                       xhrCallbacks[ handle ] = callback;
+                                               }
+                                               xhr.onreadystatechange = callback;
+                                       }
+                               },
+
+                               abort: function() {
+                                       if ( callback ) {
+                                               callback(0,1);
+                                       }
+                               }
+                       };
+               }
+       });
+}
+var fxNow, timerId,
+       rfxtypes = /^(?:toggle|show|hide)$/,
+       rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+       rrun = /queueHooks$/,
+       animationPrefilters = [ defaultPrefilter ],
+       tweeners = {
+               "*": [function( prop, value ) {
+                       var end, unit, prevScale,
+                               tween = this.createTween( prop, value ),
+                               parts = rfxnum.exec( value ),
+                               target = tween.cur(),
+                               start = +target || 0,
+                               scale = 1;
+
+                       if ( parts ) {
+                               end = +parts[2];
+                               unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+                               // We need to compute starting value
+                               if ( unit !== "px" && start ) {
+                                       // Iteratively approximate from a nonzero starting point
+                                       // Prefer the current property, because this process will be trivial if it uses the same units
+                                       // Fallback to end or a simple constant
+                                       start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+                                       do {
+                                               // If previous iteration zeroed out, double until we get *something*
+                                               // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+                                               prevScale = scale = scale || ".5";
+
+                                               // Adjust and apply
+                                               start = start / scale;
+                                               jQuery.style( tween.elem, prop, start + unit );
+
+                                               // Update scale, tolerating zeroes from tween.cur()
+                                               scale = tween.cur() / target;
+
+                                       // Stop looping if we've hit the mark or scale is unchanged
+                                       } while ( scale !== 1 && scale !== prevScale );
+                               }
+
+                               tween.unit = unit;
+                               tween.start = start;
+                               // If a +=/-= token was provided, we're doing a relative animation
+                               tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+                       }
+                       return tween;
+               }]
+       };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+       setTimeout(function() {
+               fxNow = undefined;
+       }, 0 );
+       return ( fxNow = jQuery.now() );
+}
+
+function createTweens( animation, props ) {
+       jQuery.each( props, function( prop, value ) {
+               var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+                       index = 0,
+                       length = collection.length;
+               for ( ; index < length; index++ ) {
+                       if ( collection[ index ].call( animation, prop, value ) ) {
+
+                               // we're done with this property
+                               return;
+                       }
+               }
+       });
+}
+
+function Animation( elem, properties, options ) {
+       var result,
+               index = 0,
+               tweenerIndex = 0,
+               length = animationPrefilters.length,
+               deferred = jQuery.Deferred().always( function() {
+                       // don't match elem in the :animated selector
+                       delete tick.elem;
+               }),
+               tick = function() {
+                       var currentTime = fxNow || createFxNow(),
+                               remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+                               percent = 1 - ( remaining / animation.duration || 0 ),
+                               index = 0,
+                               length = animation.tweens.length;
+
+                       for ( ; index < length ; index++ ) {
+                               animation.tweens[ index ].run( percent );
+                       }
+
+                       deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+                       if ( percent < 1 && length ) {
+                               return remaining;
+                       } else {
+                               deferred.resolveWith( elem, [ animation ] );
+                               return false;
+                       }
+               },
+               animation = deferred.promise({
+                       elem: elem,
+                       props: jQuery.extend( {}, properties ),
+                       opts: jQuery.extend( true, { specialEasing: {} }, options ),
+                       originalProperties: properties,
+                       originalOptions: options,
+                       startTime: fxNow || createFxNow(),
+                       duration: options.duration,
+                       tweens: [],
+                       createTween: function( prop, end, easing ) {
+                               var tween = jQuery.Tween( elem, animation.opts, prop, end,
+                                               animation.opts.specialEasing[ prop ] || animation.opts.easing );
+                               animation.tweens.push( tween );
+                               return tween;
+                       },
+                       stop: function( gotoEnd ) {
+                               var index = 0,
+                                       // if we are going to the end, we want to run all the tweens
+                                       // otherwise we skip this part
+                                       length = gotoEnd ? animation.tweens.length : 0;
+
+                               for ( ; index < length ; index++ ) {
+                                       animation.tweens[ index ].run( 1 );
+                               }
+
+                               // resolve when we played the last frame
+                               // otherwise, reject
+                               if ( gotoEnd ) {
+                                       deferred.resolveWith( elem, [ animation, gotoEnd ] );
+                               } else {
+                                       deferred.rejectWith( elem, [ animation, gotoEnd ] );
+                               }
+                               return this;
+                       }
+               }),
+               props = animation.props;
+
+       propFilter( props, animation.opts.specialEasing );
+
+       for ( ; index < length ; index++ ) {
+               result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+               if ( result ) {
+                       return result;
+               }
+       }
+
+       createTweens( animation, props );
+
+       if ( jQuery.isFunction( animation.opts.start ) ) {
+               animation.opts.start.call( elem, animation );
+       }
+
+       jQuery.fx.timer(
+               jQuery.extend( tick, {
+                       anim: animation,
+                       queue: animation.opts.queue,
+                       elem: elem
+               })
+       );
+
+       // attach callbacks from options
+       return animation.progress( animation.opts.progress )
+               .done( animation.opts.done, animation.opts.complete )
+               .fail( animation.opts.fail )
+               .always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+       var index, name, easing, value, hooks;
+
+       // camelCase, specialEasing and expand cssHook pass
+       for ( index in props ) {
+               name = jQuery.camelCase( index );
+               easing = specialEasing[ name ];
+               value = props[ index ];
+               if ( jQuery.isArray( value ) ) {
+                       easing = value[ 1 ];
+                       value = props[ index ] = value[ 0 ];
+               }
+
+               if ( index !== name ) {
+                       props[ name ] = value;
+                       delete props[ index ];
+               }
+
+               hooks = jQuery.cssHooks[ name ];
+               if ( hooks && "expand" in hooks ) {
+                       value = hooks.expand( value );
+                       delete props[ name ];
+
+                       // not quite $.extend, this wont overwrite keys already present.
+                       // also - reusing 'index' from above because we have the correct "name"
+                       for ( index in value ) {
+                               if ( !( index in props ) ) {
+                                       props[ index ] = value[ index ];
+                                       specialEasing[ index ] = easing;
+                               }
+                       }
+               } else {
+                       specialEasing[ name ] = easing;
+               }
+       }
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+       tweener: function( props, callback ) {
+               if ( jQuery.isFunction( props ) ) {
+                       callback = props;
+                       props = [ "*" ];
+               } else {
+                       props = props.split(" ");
+               }
+
+               var prop,
+                       index = 0,
+                       length = props.length;
+
+               for ( ; index < length ; index++ ) {
+                       prop = props[ index ];
+                       tweeners[ prop ] = tweeners[ prop ] || [];
+                       tweeners[ prop ].unshift( callback );
+               }
+       },
+
+       prefilter: function( callback, prepend ) {
+               if ( prepend ) {
+                       animationPrefilters.unshift( callback );
+               } else {
+                       animationPrefilters.push( callback );
+               }
+       }
+});
+
+function defaultPrefilter( elem, props, opts ) {
+       var index, prop, value, length, dataShow, tween, hooks, oldfire,
+               anim = this,
+               style = elem.style,
+               orig = {},
+               handled = [],
+               hidden = elem.nodeType && isHidden( elem );
+
+       // handle queue: false promises
+       if ( !opts.queue ) {
+               hooks = jQuery._queueHooks( elem, "fx" );
+               if ( hooks.unqueued == null ) {
+                       hooks.unqueued = 0;
+                       oldfire = hooks.empty.fire;
+                       hooks.empty.fire = function() {
+                               if ( !hooks.unqueued ) {
+                                       oldfire();
+                               }
+                       };
+               }
+               hooks.unqueued++;
+
+               anim.always(function() {
+                       // doing this makes sure that the complete handler will be called
+                       // before this completes
+                       anim.always(function() {
+                               hooks.unqueued--;
+                               if ( !jQuery.queue( elem, "fx" ).length ) {
+                                       hooks.empty.fire();
+                               }
+                       });
+               });
+       }
+
+       // height/width overflow pass
+       if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+               // Make sure that nothing sneaks out
+               // Record all 3 overflow attributes because IE does not
+               // change the overflow attribute when overflowX and
+               // overflowY are set to the same value
+               opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+               // Set display property to inline-block for height/width
+               // animations on inline elements that are having width/height animated
+               if ( jQuery.css( elem, "display" ) === "inline" &&
+                               jQuery.css( elem, "float" ) === "none" ) {
+
+                       // inline-level elements accept inline-block;
+                       // block-level elements need to be inline with layout
+                       if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+                               style.display = "inline-block";
+
+                       } else {
+                               style.zoom = 1;
+                       }
+               }
+       }
+
+       if ( opts.overflow ) {
+               style.overflow = "hidden";
+               if ( !jQuery.support.shrinkWrapBlocks ) {
+                       anim.done(function() {
+                               style.overflow = opts.overflow[ 0 ];
+                               style.overflowX = opts.overflow[ 1 ];
+                               style.overflowY = opts.overflow[ 2 ];
+                       });
+               }
+       }
+
+
+       // show/hide pass
+       for ( index in props ) {
+               value = props[ index ];
+               if ( rfxtypes.exec( value ) ) {
+                       delete props[ index ];
+                       if ( value === ( hidden ? "hide" : "show" ) ) {
+                               continue;
+                       }
+                       handled.push( index );
+               }
+       }
+
+       length = handled.length;
+       if ( length ) {
+               dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+               if ( hidden ) {
+                       jQuery( elem ).show();
+               } else {
+                       anim.done(function() {
+                               jQuery( elem ).hide();
+                       });
+               }
+               anim.done(function() {
+                       var prop;
+                       jQuery.removeData( elem, "fxshow", true );
+                       for ( prop in orig ) {
+                               jQuery.style( elem, prop, orig[ prop ] );
+                       }
+               });
+               for ( index = 0 ; index < length ; index++ ) {
+                       prop = handled[ index ];
+                       tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+                       orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+                       if ( !( prop in dataShow ) ) {
+                               dataShow[ prop ] = tween.start;
+                               if ( hidden ) {
+                                       tween.end = tween.start;
+                                       tween.start = prop === "width" || prop === "height" ? 1 : 0;
+                               }
+                       }
+               }
+       }
+}
+
+function Tween( elem, options, prop, end, easing ) {
+       return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+       constructor: Tween,
+       init: function( elem, options, prop, end, easing, unit ) {
+               this.elem = elem;
+               this.prop = prop;
+               this.easing = easing || "swing";
+               this.options = options;
+               this.start = this.now = this.cur();
+               this.end = end;
+               this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+       },
+       cur: function() {
+               var hooks = Tween.propHooks[ this.prop ];
+
+               return hooks && hooks.get ?
+                       hooks.get( this ) :
+                       Tween.propHooks._default.get( this );
+       },
+       run: function( percent ) {
+               var eased,
+                       hooks = Tween.propHooks[ this.prop ];
+
+               this.pos = eased = jQuery.easing[ this.easing ]( percent, this.options.duration * percent, 0, 1, this.options.duration );
+               this.now = ( this.end - this.start ) * eased + this.start;
+
+               if ( this.options.step ) {
+                       this.options.step.call( this.elem, this.now, this );
+               }
+
+               if ( hooks && hooks.set ) {
+                       hooks.set( this );
+               } else {
+                       Tween.propHooks._default.set( this );
+               }
+               return this;
+       }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+       _default: {
+               get: function( tween ) {
+                       var result;
+
+                       if ( tween.elem[ tween.prop ] != null &&
+                               (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+                               return tween.elem[ tween.prop ];
+                       }
+
+                       // passing any value as a 4th parameter to .css will automatically
+                       // attempt a parseFloat and fallback to a string if the parse fails
+                       // so, simple values such as "10px" are parsed to Float.
+                       // complex values such as "rotate(1rad)" are returned as is.
+                       result = jQuery.css( tween.elem, tween.prop, false, "" );
+                       // Empty strings, null, undefined and "auto" are converted to 0.
+                       return !result || result === "auto" ? 0 : result;
+               },
+               set: function( tween ) {
+                       // use step hook for back compat - use cssHook if its there - use .style if its
+                       // available and use plain properties where available
+                       if ( jQuery.fx.step[ tween.prop ] ) {
+                               jQuery.fx.step[ tween.prop ]( tween );
+                       } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+                               jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+                       } else {
+                               tween.elem[ tween.prop ] = tween.now;
+                       }
+               }
+       }
+};
+
+// Remove in 2.0 - this supports IE8's panic based approach
+// to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+       set: function( tween ) {
+               if ( tween.elem.nodeType && tween.elem.parentNode ) {
+                       tween.elem[ tween.prop ] = tween.now;
+               }
+       }
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+       var cssFn = jQuery.fn[ name ];
+       jQuery.fn[ name ] = function( speed, easing, callback ) {
+               return speed == null || typeof speed === "boolean" ||
+                       // special check for .toggle( handler, handler, ... )
+                       ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ?
+                       cssFn.apply( this, arguments ) :
+                       this.animate( genFx( name, true ), speed, easing, callback );
+       };
+});
+
+jQuery.fn.extend({
+       fadeTo: function( speed, to, easing, callback ) {
+
+               // show any hidden elements after setting opacity to 0
+               return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+                       // animate to the value specified
+                       .end().animate({ opacity: to }, speed, easing, callback );
+       },
+       animate: function( prop, speed, easing, callback ) {
+               var empty = jQuery.isEmptyObject( prop ),
+                       optall = jQuery.speed( speed, easing, callback ),
+                       doAnimation = function() {
+                               // Operate on a copy of prop so per-property easing won't be lost
+                               var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+                               // Empty animations resolve immediately
+                               if ( empty ) {
+                                       anim.stop( true );
+                               }
+                       };
+
+               return empty || optall.queue === false ?
+                       this.each( doAnimation ) :
+                       this.queue( optall.queue, doAnimation );
+       },
+       stop: function( type, clearQueue, gotoEnd ) {
+               var stopQueue = function( hooks ) {
+                       var stop = hooks.stop;
+                       delete hooks.stop;
+                       stop( gotoEnd );
+               };
+
+               if ( typeof type !== "string" ) {
+                       gotoEnd = clearQueue;
+                       clearQueue = type;
+                       type = undefined;
+               }
+               if ( clearQueue && type !== false ) {
+                       this.queue( type || "fx", [] );
+               }
+
+               return this.each(function() {
+                       var dequeue = true,
+                               index = type != null && type + "queueHooks",
+                               timers = jQuery.timers,
+                               data = jQuery._data( this );
+
+                       if ( index ) {
+                               if ( data[ index ] && data[ index ].stop ) {
+                                       stopQueue( data[ index ] );
+                               }
+                       } else {
+                               for ( index in data ) {
+                                       if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+                                               stopQueue( data[ index ] );
+                                       }
+                               }
+                       }
+
+                       for ( index = timers.length; index--; ) {
+                               if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+                                       timers[ index ].anim.stop( gotoEnd );
+                                       dequeue = false;
+                                       timers.splice( index, 1 );
+                               }
+                       }
+
+                       // start the next in the queue if the last step wasn't forced
+                       // timers currently will call their complete callbacks, which will dequeue
+                       // but only if they were gotoEnd
+                       if ( dequeue || !gotoEnd ) {
+                               jQuery.dequeue( this, type );
+                       }
+               });
+       }
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+       var which,
+               attrs = { height: type },
+               i = 0;
+
+       // if we include width, step value is 1 to do all cssExpand values,
+       // if we don't include width, step value is 2 to skip over Left and Right
+       for( ; i < 4 ; i += 2 - includeWidth ) {
+               which = cssExpand[ i ];
+               attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+       }
+
+       if ( includeWidth ) {
+               attrs.opacity = attrs.width = type;
+       }
+
+       return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: genFx("show"),
+       slideUp: genFx("hide"),
+       slideToggle: genFx("toggle"),
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" },
+       fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+       jQuery.fn[ name ] = function( speed, easing, callback ) {
+               return this.animate( props, speed, easing, callback );
+       };
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+       var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+               complete: fn || !fn && easing ||
+                       jQuery.isFunction( speed ) && speed,
+               duration: speed,
+               easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+       };
+
+       opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+               opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+       // normalize opt.queue - true/undefined/null -> "fx"
+       if ( opt.queue == null || opt.queue === true ) {
+               opt.queue = "fx";
+       }
+
+       // Queueing
+       opt.old = opt.complete;
+
+       opt.complete = function() {
+               if ( jQuery.isFunction( opt.old ) ) {
+                       opt.old.call( this );
+               }
+
+               if ( opt.queue ) {
+                       jQuery.dequeue( this, opt.queue );
+               }
+       };
+
+       return opt;
+};
+
+jQuery.easing = {
+       linear: function( p ) {
+               return p;
+       },
+       swing: function( p ) {
+               return 0.5 - Math.cos( p*Math.PI ) / 2;
+       }
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+       var timer,
+               timers = jQuery.timers,
+               i = 0;
+
+       for ( ; i < timers.length; i++ ) {
+               timer = timers[ i ];
+               // Checks the timer has not already been removed
+               if ( !timer() && timers[ i ] === timer ) {
+                       timers.splice( i--, 1 );
+               }
+       }
+
+       if ( !timers.length ) {
+               jQuery.fx.stop();
+       }
+};
+
+jQuery.fx.timer = function( timer ) {
+       if ( timer() && jQuery.timers.push( timer ) && !timerId ) {
+               timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+       }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.stop = function() {
+       clearInterval( timerId );
+       timerId = null;
+};
+
+jQuery.fx.speeds = {
+       slow: 600,
+       fast: 200,
+       // Default speed
+       _default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.animated = function( elem ) {
+               return jQuery.grep(jQuery.timers, function( fn ) {
+                       return elem === fn.elem;
+               }).length;
+       };
+}
+var rroot = /^(?:body|html)$/i;
+
+jQuery.fn.offset = function( options ) {
+       if ( arguments.length ) {
+               return options === undefined ?
+                       this :
+                       this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+       }
+
+       var box, docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, top, left,
+               elem = this[ 0 ],
+               doc = elem && elem.ownerDocument;
+
+       if ( !doc ) {
+               return;
+       }
+
+       if ( (body = doc.body) === elem ) {
+               return jQuery.offset.bodyOffset( elem );
+       }
+
+       docElem = doc.documentElement;
+
+       // Make sure we're not dealing with a disconnected DOM node
+       if ( !jQuery.contains( docElem, elem ) ) {
+               return { top: 0, left: 0 };
+       }
+
+       box = elem.getBoundingClientRect();
+       win = getWindow( doc );
+       clientTop  = docElem.clientTop  || body.clientTop  || 0;
+       clientLeft = docElem.clientLeft || body.clientLeft || 0;
+       scrollTop  = win.pageYOffset || docElem.scrollTop;
+       scrollLeft = win.pageXOffset || docElem.scrollLeft;
+       top  = box.top  + scrollTop  - clientTop;
+       left = box.left + scrollLeft - clientLeft;
+
+       return { top: top, left: left };
+};
+
+jQuery.offset = {
+
+       bodyOffset: function( body ) {
+               var top = body.offsetTop,
+                       left = body.offsetLeft;
+
+               if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+                       top  += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+                       left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+               }
+
+               return { top: top, left: left };
+       },
+
+       setOffset: function( elem, options, i ) {
+               var position = jQuery.css( elem, "position" );
+
+               // set position first, in-case top/left are set even on static elem
+               if ( position === "static" ) {
+                       elem.style.position = "relative";
+               }
+
+               var curElem = jQuery( elem ),
+                       curOffset = curElem.offset(),
+                       curCSSTop = jQuery.css( elem, "top" ),
+                       curCSSLeft = jQuery.css( elem, "left" ),
+                       calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+                       props = {}, curPosition = {}, curTop, curLeft;
+
+               // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+               if ( calculatePosition ) {
+                       curPosition = curElem.position();
+                       curTop = curPosition.top;
+                       curLeft = curPosition.left;
+               } else {
+                       curTop = parseFloat( curCSSTop ) || 0;
+                       curLeft = parseFloat( curCSSLeft ) || 0;
+               }
+
+               if ( jQuery.isFunction( options ) ) {
+                       options = options.call( elem, i, curOffset );
+               }
+
+               if ( options.top != null ) {
+                       props.top = ( options.top - curOffset.top ) + curTop;
+               }
+               if ( options.left != null ) {
+                       props.left = ( options.left - curOffset.left ) + curLeft;
+               }
+
+               if ( "using" in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       }
+};
+
+
+jQuery.fn.extend({
+
+       position: function() {
+               if ( !this[0] ) {
+                       return;
+               }
+
+               var elem = this[0],
+
+               // Get *real* offsetParent
+               offsetParent = this.offsetParent(),
+
+               // Get correct offsets
+               offset       = this.offset(),
+               parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+               // Subtract element margins
+               // note: when an element has margin: auto the offsetLeft and marginLeft
+               // are the same in Safari causing offset.left to incorrectly be 0
+               offset.top  -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+               offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+               // Add offsetParent borders
+               parentOffset.top  += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+               parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+               // Subtract the two offsets
+               return {
+                       top:  offset.top  - parentOffset.top,
+                       left: offset.left - parentOffset.left
+               };
+       },
+
+       offsetParent: function() {
+               return this.map(function() {
+                       var offsetParent = this.offsetParent || document.body;
+                       while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+                               offsetParent = offsetParent.offsetParent;
+                       }
+                       return offsetParent || document.body;
+               });
+       }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+       var top = /Y/.test( prop );
+
+       jQuery.fn[ method ] = function( val ) {
+               return jQuery.access( this, function( elem, method, val ) {
+                       var win = getWindow( elem );
+
+                       if ( val === undefined ) {
+                               return win ? (prop in win) ? win[ prop ] :
+                                       win.document.documentElement[ method ] :
+                                       elem[ method ];
+                       }
+
+                       if ( win ) {
+                               win.scrollTo(
+                                       !top ? val : jQuery( win ).scrollLeft(),
+                                        top ? val : jQuery( win ).scrollTop()
+                               );
+
+                       } else {
+                               elem[ method ] = val;
+                       }
+               }, method, val, arguments.length, null );
+       };
+});
+
+function getWindow( elem ) {
+       return jQuery.isWindow( elem ) ?
+               elem :
+               elem.nodeType === 9 ?
+                       elem.defaultView || elem.parentWindow :
+                       false;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+       jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+               // margin is only for outerHeight, outerWidth
+               jQuery.fn[ funcName ] = function( margin, value ) {
+                       var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+                               extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+                       return jQuery.access( this, function( elem, type, value ) {
+                               var doc;
+
+                               if ( jQuery.isWindow( elem ) ) {
+                                       // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+                                       // isn't a whole lot we can do. See pull request at this URL for discussion:
+                                       // https://github.com/jquery/jquery/pull/764
+                                       return elem.document.documentElement[ "client" + name ];
+                               }
+
+                               // Get document width or height
+                               if ( elem.nodeType === 9 ) {
+                                       doc = elem.documentElement;
+
+                                       // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+                                       // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+                                       return Math.max(
+                                               elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+                                               elem.body[ "offset" + name ], doc[ "offset" + name ],
+                                               doc[ "client" + name ]
+                                       );
+                               }
+
+                               return value === undefined ?
+                                       // Get width or height on the element, requesting but not forcing parseFloat
+                                       jQuery.css( elem, type, value, extra ) :
+
+                                       // Set width or height on the element
+                                       jQuery.style( elem, type, value, extra );
+                       }, type, chainable ? margin : undefined, chainable );
+               };
+       });
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+       define( "jquery", [], function () { return jQuery; } );
+}
+
+})( window );
+
+$(document).ready(function(){
+/*
+       $('#system h2').addClass('hide');
+       $('#system table').addClass('hide');
+       $('#cpuidle h2').addClass('hide');
+       $('#cpuidle table').addClass('hide');
+       $('#cpufreq h2').addClass('hide');
+       $('#cpufreq table').addClass('hide');
+*/
+//     $('#software h2').addClass('hide');
+//     $('#software table').addClass('hide');
+       $('#device h2').addClass('hide');
+       $('#device table').addClass('hide');
+       $('#device p').addClass('hide');
+/*
+       $('#tuning h2').addClass('hide');
+       $('#tuning table').addClass('hide');
+
+       $('#top').append('<div class="SystemButton"   onclick="toggleSystem()">System Info</div>');
+       $('#top').append('<div class="SummaryButton"  onclick="toggleSummary()">Summary</div>');
+       $('#top').append('<div class="CpuidleButton"  onclick="toggleCpuidle()">CPU Idle</div>');
+       $('#top').append('<div class="CpufreqButton"  onclick="toggleCpufreq()">CPU Frequency</div>');
+*/
+       $('#top').append('<div class="SoftwareButton" onclick="toggleSoftware()">Software info</div>');
+       $('#top').append('<div class="DeviceButton"   onclick="toggleDevice()">Device Info</div>');
+//     $('#top').append('<div class="TuningButton"   onclick="toggleTuning()">Tuning</div>');
+       $('#top').append('<div class="AllButton"      onclick="toggleAll()">All</div>');
+//     $('#top .SummaryButton').toggleClass('pressed');
+       $('#top .SoftwareButton').toggleClass('pressed');
+
+    }
+       );
+function toggleDummy() {
+/*
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+*/
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+/*
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+*/
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+//             $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+/*
+function toggleSystem() {
+               $('#system table').toggleClass('hide', false);
+               $('#system h2').toggleClass('hide', false);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', true);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+               $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+function toggleSummary() {
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', false);
+               $('#summary h2').toggleClass('hide', false);
+               $('#summary p').toggleClass('hide', false);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', true);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+               $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+function toggleCpuidle() {
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', false);
+               $('#cpuidle h2').toggleClass('hide', false);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', true);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+               $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+
+}
+function toggleCpufreq() {
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', false);
+               $('#cpufreq h2').toggleClass('hide', false);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', true);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+               $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+*/
+function toggleSoftware() {
+/*
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+*/
+               $('#software table').toggleClass('hide', false);
+               $('#software h2').toggleClass('hide', false);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+/*
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+*/
+               $('#top .SoftwareButton').toggleClass('pressed', true);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+//             $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+
+function toggleDevice() {
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', false);
+               $('#device h2').toggleClass('hide', false);
+               $('#device p').toggleClass('hide', false);
+               $('#tuning table').toggleClass('hide', true);
+               $('#tuning h2').toggleClass('hide', true);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', true);
+               $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+/*
+function toggleTuning() {
+               $('#system table').toggleClass('hide', true);
+               $('#system h2').toggleClass('hide', true);
+               $('#summary table').toggleClass('hide', true);
+               $('#summary h2').toggleClass('hide', true);
+               $('#summary p').toggleClass('hide', true);
+               $('#cpuidle table').toggleClass('hide', true);
+               $('#cpuidle h2').toggleClass('hide', true);
+               $('#cpufreq table').toggleClass('hide', true);
+               $('#cpufreq h2').toggleClass('hide', true);
+               $('#software table').toggleClass('hide', true);
+               $('#software h2').toggleClass('hide', true);
+               $('#device table').toggleClass('hide', true);
+               $('#device h2').toggleClass('hide', true);
+               $('#device p').toggleClass('hide', true);
+               $('#tuning table').toggleClass('hide', false);
+               $('#tuning h2').toggleClass('hide', false);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+               $('#top .TuningButton').toggleClass('pressed', true);
+               $('#top .AllButton').toggleClass('pressed', false);
+}
+*/
+function toggleAll() {
+/*
+               $('#system table').toggleClass('hide', false);
+               $('#system h2').toggleClass('hide', false);
+               $('#summary table').toggleClass('hide', false);
+               $('#summary h2').toggleClass('hide', false);
+               $('#summary p').toggleClass('hide', false);
+               $('#cpuidle table').toggleClass('hide', false);
+               $('#cpuidle h2').toggleClass('hide', false);
+               $('#cpufreq table').toggleClass('hide', false);
+               $('#cpufreq h2').toggleClass('hide', false);
+*/
+               $('#software table').toggleClass('hide', false);
+               $('#software h2').toggleClass('hide', false);
+               $('#device table').toggleClass('hide', false);
+               $('#device h2').toggleClass('hide', false);
+               $('#device p').toggleClass('hide', false);
+/*
+               $('#tuning table').toggleClass('hide', false);
+               $('#tuning h2').toggleClass('hide', false);
+               $('#top .SystemButton').toggleClass('pressed', false);
+               $('#top .SummaryButton').toggleClass('pressed', false);
+               $('#top .CpuidleButton').toggleClass('pressed', false);
+               $('#top .CpufreqButton').toggleClass('pressed', false);
+*/
+               $('#top .SoftwareButton').toggleClass('pressed', false);
+               $('#top .DeviceButton').toggleClass('pressed', false);
+//             $('#top .TuningButton').toggleClass('pressed', false);
+               $('#top .AllButton').toggleClass('pressed', true);
+}
+</script>
+<style type="text/css">
+table
+{
+       background-color: #F8F8F8;
+       color: black;
+}
+
+th
+{
+       text-align: right;
+}
+th.device
+{
+       text-align: left;
+}
+th.process
+{
+       text-align: left;
+}
+
+td.package_odd
+{
+       background-color: #E0ffE0;
+       color: black;
+       text-align: right;
+}
+td.package_even
+{
+       background-color: #F0ffF0;
+       color: black;
+       text-align: right;
+}
+
+td.core_even
+{
+       background-color: #F0ffF0;
+       color: black;
+       text-align: right;
+}
+
+td.core_odd
+{
+       background-color: #E0E0ff;
+       color: black;
+       text-align: right;
+}
+
+td.cpu_even_freq
+{
+       background-color: #E2E2E2;
+       color: black;
+}
+
+td.cpu_odd_freq
+{
+       background-color: #F4f4F4;
+       color: black;
+}
+
+td.cpu_even_even
+{
+       background-color: #E0E0f0;
+       color: black;
+       text-align: right;
+}
+
+td.cpu_even_odd
+{
+       background-color: #F0f0F0;
+       color: black;
+       text-align: right;
+}
+td.cpu_odd_even
+{
+       background-color: #E0E0ff;
+       color: black;
+       text-align: right;
+}
+
+td.cpu_odd_odd
+{
+       background-color: #F0ffF0;
+       color: black;
+       text-align: right;
+}
+
+tr.device_odd
+{
+       background-color: #E0E0E0;
+       color: black;
+}
+tr.device_even
+{
+       background-color: #F0f0F0;
+       color: black;
+}
+
+tr.process_odd
+{
+       background-color: #E0E0E0;
+       color: black;
+}
+tr.process_even
+{
+       background-color: #F0f0F0;
+       color: black;
+}
+
+td.device_power
+{
+       text-align: right;
+}
+
+td.process_power
+{
+       text-align: right;
+}
+
+td.device_util
+{
+       text-align: right;
+}
+
+tr.tunable_odd
+{
+       background-color: #E0E0E0;
+       color: black;
+}
+tr.tunable_even
+{
+       background-color: #F0f0F0;
+       color: black;
+}
+
+tr.tunable_odd_bad
+{
+       background-color: #FFE0E0;
+       color: black;
+}
+tr.tunable_even_bad
+{
+       background-color: #FFf0F0;
+       color: black;
+}
+
+tr.system_odd
+{
+       background-color: #E0E0E0;
+       color: black;
+}
+tr.system_even
+{
+       background-color: #F0f0F0;
+       color: black;
+}
+
+tr.device_odd
+{
+       background-color: #E0E0E0;
+       color: black;
+}
+tr.device_even
+{
+       background-color: #F0f0F0;
+       color: black;
+}
+
+body {
+       background-color: #eee; /* Background color */
+       color: #222;            /* Foreground color used for text */
+       font-family: Helvetica;
+       font-size: 14px;
+       margin: 0;              /* Amount of negative space around the
+                                   outside of the body */
+       padding: 0;             /* Amount of negative space around the
+                                   inside of the body */
+}
+#top h1 {
+       margin: 0;
+       padding: 0;
+}
+#top h1 a {
+       background-color: #ccc;
+       border-bottom: 1px solid #666;
+       color: #222;
+       display: block;
+       font-size: 20px;
+       font-weight: bold;
+       padding: 10px 0;
+       text-align: center;
+       text-decoration: none;
+       text-shadow: 0px 1px 1px #fff;
+       background-image: -webkit-gradient(linear, left top, left bottom,
+                                               from(#ccc), to(#999));
+}
+
+
+#top div {
+       height: 30px;
+       font-weight: bold;
+       text-align: center;
+       color: white;
+       text-shadow: rgba(0,0,0,0.6) 0px -1px 1px;
+       line-height: 28px;
+       border-width:0px 8px 0px 8px;
+}
+#top div.SystemButton {
+       position: absolute;
+       top: 7px;
+       left: 6px;
+}
+#top div.SummaryButton {
+       position: absolute;
+       top: 7px;
+       left: 160px;
+}
+#top div.CpuidleButton {
+       position: absolute;
+       top: 7px;
+       left: 320px;
+}
+#top div.CpufreqButton {
+       position: absolute;
+       top: 7px;
+       left: 480px;
+}
+#top div.SoftwareButton {
+       position: absolute;
+       top: 7px;
+/*     left: 640px;*/
+       left: 6px;
+}
+#top div.DeviceButton {
+       position: absolute;
+       top: 7px;
+/*     left: 800px;*/
+       left: 160px;
+}
+#top div.TuningButton {
+       position: absolute;
+       top: 7px;
+       left: 960px;
+}
+#top div.AllButton {
+       position: absolute;
+       top: 7px;
+/*     left: 1120px;*/
+       left: 320px;
+}
+div.pressed {
+       background-color: #000000;
+       border: 1px solid #000000;
+       color: #000000;
+       display: block;
+       border-width:0px 8px 0px 8px;
+       -webkit-border-top-left-radius: 8px;
+       -webkit-border-top-right-radius: 8px;
+       -webkit-border-bottom-left-radius: 8px;
+       -webkit-border-bottom-right-radius: 8px;
+}
+table.hide {
+       display: none;
+}
+h2.hide {
+       display: none;
+}
+p.hide {
+       display: none
+}
+</style>
+</head>
+
+<body>
+<div id="top">
+<h1><a href="#top">&nbsp;</a></h1>
+</div>
diff --git a/src/powertop-wrapper/powertop-wrapper.cpp b/src/powertop-wrapper/powertop-wrapper.cpp
new file mode 100644 (file)
index 0000000..fa263a6
--- /dev/null
@@ -0,0 +1,805 @@
+/*
+ * Library for getting power usage statistics
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * Contact: Igor Zhbanov <i.zhbanov@samsung.com>
+ *
+ * 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 _BSD_SOURCE
+
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <map>
+#include <string>
+
+#include <powertop-dapi.h>
+#include <powertop-wrapper.h>
+
+
+#define LINE_SIZE 4096
+
+static FILE *log = stderr;
+static volatile bool stopping = false, running = false;
+static int check_interval = 3;
+static pthread_t thr;
+static void (*t_callback)(void *) = NULL;
+static void *t_arg = NULL;
+
+/* ************************************************************************ */
+
+static void
+logprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+static void
+logprintf(const char *fmt, ...)
+{
+        va_list ap;
+       char timebuf[128];
+       time_t t;
+       struct tm *tmp;
+
+       if (!log)
+               return;
+
+       t = time(NULL);
+       tmp = localtime(&t);
+
+        va_start(ap, fmt);
+       strftime(timebuf, sizeof(timebuf), "[%Y-%m-%d %H:%M:%S] ", tmp);
+       fputs(timebuf, log);
+       vfprintf(log, fmt, ap);
+       fputc('\n', log);
+       fflush(log);
+       va_end(ap);
+}
+
+/* ************************************************************************ */
+
+static int
+loginit(const char *logname)
+{
+       if (!strcmp(logname, "STDERR")) {
+               log = stderr;
+               return 0;
+       }
+
+       log = fopen(logname, "a");
+       if (!log) {
+               perror("Can't open the log file");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* ************************************************************************ */
+
+static void
+logclose(void)
+{
+       if (log && log != stderr)
+               fclose(log);
+}
+
+/* ************************************************************************ */
+
+static bool
+get_double(char **str, double *d)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       *d = atof(*str);
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_pid_t(char **str, pid_t *p)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       *p = (pid_t)atol(*str);
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_percent(char **str, double *d)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       if (!strchr(*str, '%')) {
+               *comma = ',';
+               return false;
+       }
+
+       *d = atof(*str);
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_packets(char **str, double *usage)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       if (sscanf(*str, "%lf pkts/s", usage) != 1) {
+               *comma = ',';
+               return false;
+       }
+
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_usage(char **str, double *usage)
+{
+       char *comma, suffix[10];
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       if (sscanf(*str, "%lf %9s", usage, suffix) != 2) {
+               *comma = ',';
+               return false;
+       }
+
+       if (!strcmp(suffix, "ms/s"))
+               *usage /= 1000.;
+       else if (!strcmp(suffix, "us/s"))
+               *usage /= 1000000.;
+       else {
+               *comma = ',';
+               return false;
+       }
+
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_disks(char **str, double *harddisk, double *disk)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       *harddisk = 0;
+       *disk = 0;
+       sscanf(*str, "%lf (%lf)", harddisk, disk);
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_string(char **str, char **s)
+{
+       char *comma;
+
+       if (!(comma = strchr(*str, ',')))
+               return false;
+
+       *comma = '\0';
+       *s = *str;
+       while (**s == ' ')
+               (*s)++;
+
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+static bool
+get_qstring(char **str, char **s)
+{
+       char *comma;
+
+       comma = strchr(*str, ',');
+       if (comma) {
+               *comma = '\0';
+               if (comma[-1] == '"')
+                       comma[-1] = '\0';
+       }
+
+       *s = *str;
+       while (**s == ' ')
+               (*s)++;
+
+       if (**s == '"')
+               (*s)++;
+
+       *str = comma + 1;
+       return true;
+}
+
+/* ************************************************************************ */
+
+/* Will modify the line */
+static void
+update_software_power_consumers(char *line, work_ctx *ctx)
+{
+       double usage, wakeups, gpu, harddisk, disk, gfx;
+       char *category, *description, *newdescr, buf[LINE_SIZE], *str;
+
+       snprintf(buf, sizeof(buf), "%s", line);
+       str = buf;
+
+       if (!get_usage(&str, &usage))
+               return; /* Skip invalid or header line */
+
+       if (!get_double(&str, &wakeups))
+               return;
+
+       if (!get_double(&str, &gpu))
+               return;
+
+       if (!get_disks(&str, &harddisk, &disk))
+               return;
+
+       if (!get_double(&str, &gfx))
+               return;
+
+       if (!get_string(&str, &category))
+               return;
+
+       if (!get_string(&str, &description))
+               return;
+
+       newdescr = strdup(description);
+       sw_power_consumer &consumer = ctx->swpc[newdescr];
+       consumer.usage    += usage;
+       consumer.wakeups  += wakeups;
+       consumer.gpu      += gpu;
+       consumer.harddisk += harddisk;
+       consumer.disk     += disk;
+       consumer.gfx      += gfx;
+       if (!consumer.category)
+               consumer.category = strdup(category);
+
+       if (!consumer.description)
+               consumer.description = newdescr;
+       else
+               free(newdescr);
+}
+
+/* ************************************************************************ */
+
+/* Will modify the line */
+static void
+update_device_power_report(const char *line, work_ctx *ctx)
+{
+       double usage;
+       char *device, *newdevice, buf[LINE_SIZE], *str;
+       bool network = false;
+
+       snprintf(buf, sizeof(buf), "%s", line);
+       str = buf;
+
+       if (!get_percent(&str, &usage)) {
+               if (!get_packets(&str, &usage))
+                       return; /* Skip invalid or header line */
+               else
+                       network = true;
+       }
+
+       if (!get_qstring(&str, &device))
+               return;
+
+       newdevice = strdup(device);
+       hw_power_consumer &consumer = ctx->hwpc[newdevice];
+       consumer.network = network;
+       consumer.usage += usage;
+       if (!consumer.device)
+               consumer.device = newdevice;
+       else
+               free(newdevice);
+}
+
+/* ************************************************************************ */
+
+static void
+update_mali_gpu_power_consumers(const char *line, work_ctx *ctx)
+{
+       double usage;
+       pid_t pid;
+       char *name, *description, buf[LINE_SIZE], *str;
+
+       snprintf(buf, sizeof(buf), "%s", line);
+       str = buf;
+
+       if (!get_double(&str, &usage))
+               return; /* Skip invalid or header line */
+
+       if (!get_pid_t(&str, &pid))
+               return;
+       if (!pid)
+               return; /* Skip header */
+
+       if (!get_qstring(&str, &name))
+               return;
+
+       if (!get_qstring(&str, &description))
+               return;
+
+       mali_power_consumer &consumer = ctx->malipc[pid];
+       consumer.pid = pid;
+       consumer.usage += usage;
+
+       if (!consumer.name)
+               consumer.name = strdup(name);
+
+       if (!consumer.description)
+               consumer.description = strdup(description);
+}
+
+/* ************************************************************************ */
+
+static void
+process_report(const char *report, work_ctx *ctx)
+{
+       char line[LINE_SIZE];
+       const char *rep = report, *eol;
+       bool in_section = false;
+       sections section = S_UNKNOWN;
+
+       if (!report)
+               return;
+
+       rep = strchr(rep, '\n') + 1; /* Skip the first line. */
+       while ((eol = strchr(rep, '\n')) != NULL) {
+               ssize_t len;
+
+               len = eol + 1 - rep;
+               if (len > LINE_SIZE - 1)
+                       len = LINE_SIZE - 1;
+
+               strncpy(line, rep, len);
+               line[len] = '\0';
+               rep += len;
+
+               if (in_section) {
+                       if (line[0] == '*')
+                               in_section = false;
+                       else if (line[0] == '\n')
+                               continue;
+                       else if (section == S_SOFTWARE_POWER_CONSUMERS)
+                               update_software_power_consumers(line, ctx);
+                       else if (section == S_DEVICE_POWER_REPORT)
+                               update_device_power_report(line, ctx);
+                       else if (section == S_MALI_GPU_POWER_CONSUMERS)
+                               update_mali_gpu_power_consumers(line, ctx);
+               }
+
+               if (!in_section) {
+                       if (line[0] != '*')
+                               continue; /* Wrong line */
+                       else if (!strcmp(line, "**Overview of Software "
+                                         "Power Consumers**, \n"))
+                               section = S_SOFTWARE_POWER_CONSUMERS;
+                       else if (!strcmp(line, "**Device Power "
+                                                "Report**,\n"))
+                               section = S_DEVICE_POWER_REPORT;
+                       else if (!strcmp(line, "** Overview of MALI GPU "
+                                                "power consumers **\n"))
+                               section = S_MALI_GPU_POWER_CONSUMERS;
+                       else
+                               section = S_UNKNOWN;
+
+                       in_section = true;
+               }
+
+       }
+
+       free((void *)report);
+}
+
+/* ************************************************************************ */
+
+static int
+collect_reports(const char *cmdline, work_ctx *ctx)
+{
+       char line[LINE_SIZE], buf[256], *report = NULL;
+       FILE *f;
+
+       f = popen(cmdline, "r");
+       if (!f) {
+               strerror_r(errno, buf, sizeof(buf));
+               logprintf("Can't open pipe to powertop: %s",
+                         buf);
+               return -1;
+       }
+
+       while (fgets(line, sizeof(line), f)) {
+               size_t len;
+
+               len = strlen(line);
+               if (!strcmp(line, "***PowerTOP Report***, \n")) {
+                       /* Try to process incomplete report */
+                       process_report(report, ctx);
+                       report = strndup(line, len+1);
+                       if (!report) {
+                               logprintf("No memory!");
+                               pclose(f);
+                               return -1;
+                       }
+               } else if (!strcmp(line, "***End of report***\n")) {
+                       process_report(report, ctx);
+                       report = NULL;
+               } else if (report) {
+                       char *oldreport;
+
+                       oldreport = report;
+                       report = (char *)realloc(report,
+                                                strlen(report) + len + 1);
+                       if (!report) {
+                               logprintf("No memory!");
+                               free(oldreport);
+                               pclose(f);
+                               return -1;
+                       }
+
+                       strcat(report, line);
+               } /* else skip unknown line */
+       }
+
+       pclose(f);
+       process_report(report, ctx); /* Try to process incomplete report */
+       return 0;
+}
+
+/* ************************************************************************ */
+
+static void
+clear_context(work_ctx *ctx)
+{
+       sw_power_consumer_map::iterator sit;
+       hw_power_consumer_map::iterator hit;
+       mali_power_consumer_map::iterator mit;
+
+       for (sit = ctx->swpc.begin(); sit != ctx->swpc.end(); sit++) {
+               sw_power_consumer &swpc = sit->second;
+               if (swpc.description)
+                       free(swpc.description);
+
+               if (swpc.category)
+                       free(swpc.category);
+       }
+
+       ctx->swpc.clear();
+
+       for (hit = ctx->hwpc.begin(); hit != ctx->hwpc.end(); hit++) {
+               hw_power_consumer &hwpc = hit->second;
+               if (hwpc.device)
+                       free(hwpc.device);
+       }
+
+       ctx->hwpc.clear();
+
+       for (mit = ctx->malipc.begin(); mit != ctx->malipc.end(); mit++) {
+               mali_power_consumer &malipc = mit->second;
+               if (malipc.name)
+                       free(malipc.name);
+
+               if (malipc.description)
+                       free(malipc.description);
+       }
+
+       ctx->malipc.clear();
+       ctx->iterations = 0;
+}
+
+/* ************************************************************************ */
+
+static char fbuf[65536];
+
+static void
+report_write_header(FILE *out)
+{
+       FILE *in;
+       size_t len;
+
+       in = fopen(REPORTHEADER, "r");
+       if (!in) {
+               logprintf("Can't open header file '%s'.", REPORTHEADER);
+               return;
+       }
+
+       while ((len = fread(fbuf, 1, sizeof(fbuf), in)) > 0)
+               fwrite(fbuf, 1, len, out);
+
+       fclose(in);
+}
+
+/* ************************************************************************ */
+
+static void
+report_write_footer(FILE *f)
+{
+       fprintf(f,
+               "</body>\n"
+               "</html>\n");
+}
+
+/* ************************************************************************ */
+
+static void
+report_write_swpc(work_ctx *ctx, FILE *f) /* Software Power Consumers */
+{
+       int n = ctx->iterations, i;
+       sw_power_consumer_map::iterator it;
+
+       fprintf(f,
+               "<div id=\"software\"><h2>Overview of Software Power"
+                       " Consumers</h2>\n"
+               "<table width=\"100%%\">"
+               "<tr><th width=\"10%%\">Usage</th>"
+                       "<th width=\"10%%\">Wakeups/s</th>"
+                       "<th width=\"10%%\">GFX Wakeups/s</th>"
+                       "<th width=\"10%%\" class=\"process\">Category</th>"
+                       "<th class=\"process\">Description</th></tr>\n");
+       for (i = 0, it = ctx->swpc.begin(); it != ctx->swpc.end(); it++, i++) {
+               sw_power_consumer &swpc = it->second;
+
+               fprintf(f,
+                       "<tr class=\"process_%s\">"
+                               "<td class=\"process_power\">%g</td>"
+                               "<td class=\"process_power\">%g</td>"
+                               "<td class=\"process_power\">%g</td>"
+                               "<td>%s</td><td>%s</td></tr>\n",
+                       (i & 1 ? "even" : "odd"),
+                       swpc.usage / n, swpc.wakeups / n, swpc.gfx / n,
+                       swpc.category, swpc.description);
+       }
+
+       fprintf(f,
+               "</table>\n"
+               "</div>\n");
+}
+
+/* ************************************************************************ */
+
+static void
+report_write_hwpc(work_ctx *ctx, FILE *f) /* Device Power Consumers */
+{
+       int n = ctx->iterations, i;
+       hw_power_consumer_map::iterator it;
+
+       fprintf(f,
+               "<h2>Device Power Report</h2>\n"
+               "<table width=\"100%%\">\n"
+               "<tr><th width=\"10%%\">Usage</th>"
+                       "<th class=\"device\">Device name</th></tr>\n");
+       for (i = 0, it = ctx->hwpc.begin(); it != ctx->hwpc.end();
+            it++, i++) {
+               hw_power_consumer &hwpc = it->second;
+
+
+               fprintf(f,
+                       "<tr class=\"device_%s\">"
+                               "<td class=\"device_util\">%g%s</td>"
+                               "<td>%s</td></tr>\n",
+                       (i & 1 ? "even" : "odd"), hwpc.usage / n,
+                       (hwpc.network ? " pkts/s" : "%"), hwpc.device);
+       }
+
+       fprintf(f, "</table>\n");
+}
+
+/* ************************************************************************ */
+
+static void
+report_write_malipc(work_ctx *ctx, FILE *f) /* MALI GPU Power Consumers */
+{
+       int n = ctx->iterations, i;
+       mali_power_consumer_map::iterator it;
+
+       fprintf(f,
+               "<h2>Overview of MALI GPU power consumers</h2>\n"
+               "<p><table width='100%%'>\n"
+               "<tr><th width='10%%' class='process'>Power est. "
+                       "(PseudoWatts&middot;s)</th>"
+                       "<th class='process'>PID</th>"
+                       "<th class='process'>Name</th>"
+                       "<th class='process'>Description</th></tr>\n");
+       for (i = 0, it = ctx->malipc.begin(); it != ctx->malipc.end();
+            it++, i++) {
+               mali_power_consumer &malipc = it->second;
+
+               fprintf(f,
+                       "<tr class='process_%s'>"
+                       "<td class='process_power'>%g PWs</td><td>%ld</td>"
+                       "<td>%s</td><td>%s</td></tr>\n",
+                       (i & 1 ? "even" : "odd"), malipc.usage / n,
+                       (long)malipc.pid, malipc.name, malipc.description);
+       }
+
+       fprintf(f, "</table>\n");
+}
+
+/* ************************************************************************ */
+
+static void
+write_report(work_ctx *ctx)
+{
+       FILE *f;
+
+       f = fopen(ctx->report_file, "w+");
+       if (!f) {
+               logprintf("Can't open report file '%s'.",
+                         ctx->report_file);
+               return;
+       }
+
+       report_write_header(f);
+       report_write_swpc(ctx, f);
+       fprintf(f, "<div id=\"device\">\n");
+       report_write_hwpc(ctx, f);
+       report_write_malipc(ctx, f);
+       fprintf(f,"</div>\n");
+       report_write_footer(f);
+       fclose(f);
+}
+
+/* ************************************************************************ */
+
+static void *
+thread_main(void *d)
+{
+       work_ctx ctx;
+       char *oldlocale, cmdline[8192];
+
+/*     logprintf("Thread started.");*/
+       snprintf(cmdline, sizeof(cmdline),
+               "LC_NUMERIC=en %s -D -i 1 --time=%d --csv 2>/dev/null",
+               POWERTOP, check_interval);
+
+       oldlocale = setlocale(LC_NUMERIC, NULL);
+       oldlocale = strdup(oldlocale);
+       setlocale(LC_NUMERIC, "C"); /* For dots in %f in printf() */
+
+       ctx.report_file = (char *)d;
+       unlink(ctx.report_file);
+       ctx.iterations = 0;
+       while (running) {
+               if (collect_reports(cmdline, &ctx) == -1) {
+/*                     logprintf("Powertop error");*/
+                       running = false;
+                       break;
+               }
+
+               ctx.iterations++;
+       }
+
+       if (ctx.iterations) /* Generate total report */
+               write_report(&ctx);
+
+       clear_context(&ctx);
+
+       setlocale(LC_NUMERIC, oldlocale);
+       free(oldlocale);
+
+       if (t_callback)
+               t_callback(t_arg);
+
+/*     logprintf("Thread finished.");*/
+       return NULL;
+}
+
+/* ************************************************************************ */
+
+void
+powertop_set_check_interval(unsigned int interval)
+{
+       check_interval = interval;
+}
+
+/* ************************************************************************ */
+
+bool
+powertop_start_check(const char *output_path)
+{
+       pthread_attr_t thr_attr;
+
+       if (loginit(LOGFILE) == -1)
+               return false;
+
+       if (running) {
+               logprintf("The thread is already started.");
+               logclose();
+               return false;
+       }
+
+/*     logprintf("start_power_consumption_check()");*/
+
+       t_callback = NULL;
+       running = true;
+       pthread_attr_init(&thr_attr);
+       pthread_create(&thr, &thr_attr, &thread_main, (void *)output_path);
+       pthread_attr_destroy(&thr_attr);
+       return true;
+}
+
+/* ************************************************************************ */
+
+void
+powertop_stop_check(void)
+{
+/*     logprintf("stop_power_consumption_check()");*/
+       if (!running)
+               logprintf("The thread is not started of unexpectedly died.");
+
+       running = false;
+       pthread_join(thr, NULL);
+       logclose();
+}
+
+/* ************************************************************************ */
+
+void
+powertop_async_stop_check(void (*callback)(void *), void *arg)
+{
+/*     logprintf("async_stop_power_consumption_check()");*/
+       if (!running)
+               logprintf("The thread is not started of unexpectedly died.");
+
+       t_arg = arg;
+       t_callback = callback;
+       running = false;
+       pthread_detach(thr);
+       logclose();
+}
diff --git a/src/powertop-wrapper/powertop-wrapper.h b/src/powertop-wrapper/powertop-wrapper.h
new file mode 100644 (file)
index 0000000..9ee1a4c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Library for getting power usage statistics
+ *
+ * Copyright (c) 2000 - 2012 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 __POWERTOP_WRAPPER_H__
+#define __POWERTOP_WRAPPER_H__
+
+#include <string.h>
+
+#include <map>
+
+#define POWERTOP       "powertop"
+#define LOGFILE                "./powertop-wrapper.log"
+#define REPORTHEADER   "/usr/share/powertop-wrapper/header.html"
+
+#ifndef UNUSED
+#define UNUSED __attribute__((unused))
+#endif /* UNUSED */
+
+/* ************************************************************************ */
+
+typedef enum {
+       S_UNKNOWN,
+       S_SYSTEM_INFORMATION,
+       S_SOFTWARE_POWER_CONSUMERS,
+       S_DEVICE_POWER_REPORT,
+       S_PROCESS_DEVICE_ACTIVITY,
+       S_POWER_CONSUMPTION_SUMMARY,
+       S_SOFTWARE_SETTINGS_TUNING,
+       S_UNTUNABLE_SOFTWARE_ISSUES,
+       S_OPTIMAL_TUNED_SOFTWARE_SETTINGS,
+       S_PROCESSOR_IDLE_STATE_REPORT,
+       S_PROCESSOR_FREQUENCY_REPORT,
+       S_MALI_GPU_POWER_CONSUMERS
+} sections;
+
+/* ************************************************************************ */
+
+typedef struct {
+       /* gpu, harddisk and disk are unneeded now */
+       double usage, wakeups, gpu, harddisk, disk, gfx;
+       char *category, *description;
+} sw_power_consumer;
+
+/* ************************************************************************ */
+
+typedef struct {
+       double usage;
+       char *device;
+       bool network;
+} hw_power_consumer;
+
+/* ************************************************************************ */
+
+typedef struct {
+       double usage;
+       pid_t pid;
+       char *name, *description;
+} mali_power_consumer;
+
+/* ************************************************************************ */
+
+class power_consumer_cmp
+{
+public:
+       bool
+       operator()(const char *a, const char *b) const
+       {
+               return (strcmp(a, b) < 0);
+       }
+};
+
+/* ************************************************************************ */
+
+typedef std::map<const char *, sw_power_consumer, power_consumer_cmp>
+                                                       sw_power_consumer_map;
+typedef std::map<const char *, hw_power_consumer, power_consumer_cmp>
+                                                       hw_power_consumer_map;
+typedef std::map<pid_t, mali_power_consumer> mali_power_consumer_map;
+
+/* ************************************************************************ */
+
+typedef struct {
+       sw_power_consumer_map swpc;
+       hw_power_consumer_map hwpc;
+       mali_power_consumer_map malipc;
+       int iterations;
+       const char *report_file;
+} work_ctx;
+
+#endif /* __POWERTOP_WRAPPER_H__ */
diff --git a/src/proc-stat/CMakeLists.txt b/src/proc-stat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e3ad505
--- /dev/null
@@ -0,0 +1,70 @@
+SET(CMAKE_INSTALL_PREFIX /usr)
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR}
+       ${PROC-STAT_SOURCE_DIR}/include)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs_proc_stat REQUIRED dlog
+                                         glib-2.0)
+
+FOREACH(flag ${pkgs_proc_stat_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror")
+SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+
+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)
+
+ADD_LIBRARY(${PROC-STAT} SHARED ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROC-STAT} ${${PROC-STAT}_LDFLAGS})
+
+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)
diff --git a/src/proc-stat/include/proc-handler.h b/src/proc-stat/include/proc-handler.h
new file mode 100644 (file)
index 0000000..fc6f4d2
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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
diff --git a/src/proc-stat/include/proc-main.h b/src/proc-stat/include/proc-main.h
new file mode 100644 (file)
index 0000000..d4afec2
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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 "daemon-options.h"
+#include "resourced.h"
+#include "const.h"
+
+#define PROC_BUF_MAX 64
+#define PROC_NAME_MAX 512
+
+typedef GSList *pid_info_list;
+
+enum application_type {
+       RESOURCED_APP_TYPE_UNKNOWN,
+       RESOURCED_APP_TYPE_GUI,
+       RESOURCED_APP_TYPE_SERVICE,
+       RESOURCED_APP_TYPE_GROUP,
+};
+
+struct pid_info_t {
+       pid_t pid;
+       enum application_type type; /* not so fancy */
+};
+
+struct proc_process_info_t {
+       char appid[MAX_PATH_LENGTH];
+       char pkgname[MAX_PATH_LENGTH];
+       pid_info_list pids;
+       int proc_exclude;
+       int runtime_exclude;
+       int memcg_idx;
+       int state;
+       int type;
+};
+
+struct proc_status {
+       pid_t pid;
+       char* appid;
+       struct proc_process_info_t *processinfo;
+};
+
+enum proc_exclude_type {
+       PROC_EXCLUDE,
+       PROC_INCLUDE,
+};
+
+enum {
+       LCD_STATE_ON,
+       LCD_STATE_OFF,
+};
+
+enum proc_prelaunch_flags {
+       PROC_LARGE_HEAP = 0x01u,        /* for mark large heap */
+       PROC_SIGTERM    = 0x02u,        /* for make killer kill victim by SIGTERM */
+};
+
+extern int current_lcd_state;
+
+
+void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type);
+
+int resourced_proc_init(const struct daemon_opts *opts);
+
+/**
+ * @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 type, pid_t pid, char* app_name,  char* pkg_name);
+
+struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid);
+
+struct pid_info_t *new_pid_info(const pid_t pid, const int type);
+
+void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx);
+resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type);
+struct proc_process_info_t *proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid);
+int proc_remove_process_list(const pid_t pid);
+void proc_set_apptype(const char *appid, const char *pkgid, int type);
+
+
+#endif /*__PROC_MAIN_H__ */
diff --git a/src/proc-stat/include/proc-monitor.h b/src/proc-stat/include/proc-monitor.h
new file mode 100644 (file)
index 0000000..fba3708
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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,
+};
+
+resourced_ret_c proc_monitor_init(void);
+void proc_set_watchdog_state(int state);
+int proc_get_dbus_proc_state(void);
+
+#endif /* __RESOURCED_PROC_MONITOR_H__ */
+
diff --git a/src/proc-stat/include/proc-noti.h b/src/proc-stat/include/proc-noti.h
new file mode 100644 (file)
index 0000000..f8e0080
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  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;
+       char *path;
+       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
+
+int proc_noti_init(void);
+
+
+#endif /*__PROC_HANDLER_H__*/
diff --git a/src/proc-stat/include/proc-process.h b/src/proc-stat/include/proc-process.h
new file mode 100644 (file)
index 0000000..f118599
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  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__
+
+#include <proc_stat.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_LOCKED       (250)
+#define OOMADJ_BACKGRD_UNLOCKED     (300)
+#define OOMADJ_APP_LIMIT            OOMADJ_INIT
+#define OOMADJ_APP_MAX              (990)
+#define OOMADJ_APP_INCREASE         (30)
+#define OOMADJ_SERVICE_GAP              (10)
+#define OOMADJ_SERVICE_DEFAULT          (OOMADJ_BACKGRD_LOCKED - OOMADJ_SERVICE_GAP)
+#define OOMADJ_SERVICE_FOREGRD         (OOMADJ_FOREGRD_UNLOCKED - OOMADJ_SERVICE_GAP)
+#define OOMADJ_SERVICE_BACKGRD         (OOMADJ_BACKGRD_UNLOCKED - OOMADJ_SERVICE_GAP)
+
+
+enum proc_sweep_type {
+       PROC_SWEEP_EXCLUDE_ACTIVE,
+       PROC_SWEEP_INCLUDE_ACTIVE,
+};
+
+int proc_get_cmdline(pid_t pid, char *cmdline);
+int proc_sweep_memory(enum proc_sweep_type type, pid_t callpid);
+
+int proc_get_oom_score_adj(int pid, int *oom_score_adj);
+int proc_set_oom_score_adj(int pid, int oom_score_adj);
+
+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);
+
+pid_t find_pid_from_cmdline(char *cmdline);
+
+void proc_set_group(pid_t onwerpid, pid_t childpid);
+
+#endif /*__PROC_PROCESS_H__*/
diff --git a/src/proc-stat/proc-handler.c b/src/proc-stat/proc-handler.c
new file mode 100644 (file)
index 0000000..db4e535
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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!!");
+}
diff --git a/src/proc-stat/proc-main.c b/src/proc-stat/proc-main.c
new file mode 100644 (file)
index 0000000..01a47b7
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * 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 "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"
+
+pthread_mutex_t        proc_mutex      = PTHREAD_MUTEX_INITIALIZER;
+static GHashTable *proc_exclude_list;
+static Ecore_File_Monitor *exclude_list_monitor;
+static const unsigned int exclude_list_limit = 1024;
+static int proc_notifd;
+#define BASE_UGPATH_PREFIX "/usr/ug/bin"
+
+enum proc_state {
+       PROC_STATE_DEFAULT,
+       PROC_STATE_FOREGROUND,
+       PROC_STATE_BACKGROUND,
+};
+
+/*
+ * @brief pid_info_list is only for pid_info_t
+ */
+GSList *proc_process_list;
+
+struct pid_info_t *new_pid_info(const pid_t pid, const int type)
+{
+       struct pid_info_t *result = (struct pid_info_t *)malloc(
+                       sizeof(struct pid_info_t));
+       if (!result) {
+               _E("Malloc of new_pid_info failed\n");
+               return NULL;
+       }
+
+       result->pid = pid;
+       result->type = type;
+       return result;
+}
+
+static gint compare_pid(gconstpointer a, gconstpointer b)
+{
+       const struct pid_info_t *pida = (struct pid_info_t *)a;
+       const struct pid_info_t *pidb = (struct pid_info_t *)b;
+       return pida->pid == pidb->pid ? 0 :
+               pida->pid > pidb->pid ? 1 : -1;
+}
+
+static struct pid_info_t *find_pid_info(pid_info_list pids, const pid_t pid)
+{
+       struct pid_info_t pid_to_find = {
+               .pid = pid,
+               /* now it doesn't matter */
+               .type = RESOURCED_APP_TYPE_UNKNOWN,
+       };
+       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 pid_info_t *)(found->data);
+       return NULL;
+}
+
+void proc_add_pid_list(struct proc_process_info_t *process_info, int pid, enum application_type type)
+{
+       struct pid_info_t pid_to_find = {
+               .pid = pid,
+               /* now it doesn't matter */
+               .type = RESOURCED_APP_TYPE_UNKNOWN,
+       };
+       GSList *found = NULL;
+
+       if (process_info->pids)
+               found = g_slist_find_custom((GSList *)process_info->pids,
+                       &pid_to_find, compare_pid);
+
+       if (found)
+               return;
+
+       pthread_mutex_lock(&proc_mutex);
+       process_info->pids = g_slist_prepend(process_info->pids, new_pid_info(pid, type));
+       pthread_mutex_unlock(&proc_mutex);
+}
+
+static int equal_process_info(const char *appid_a, const char *appid_b)
+{
+       return !strcmp(appid_a, appid_b);
+}
+
+static resourced_ret_c proc_check_ug(pid_t pid)
+{
+       char buf[PROC_BUF_MAX];
+       char cmdline_buf[PROC_NAME_MAX];
+       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);
+       if (strstr(cmdline_buf, BASE_UGPATH_PREFIX)) {
+               _D("pid(%d) is ug process. don't freeze this process", pid);
+               return RESOURCED_ERROR_NONFREEZABLE;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static void proc_set_service_oomscore(const pid_t pid, const int state)
+{
+       int oom_score = OOMADJ_SERVICE_DEFAULT;
+       switch(state) {
+       case PROC_STATE_DEFAULT:
+               oom_score = OOMADJ_SERVICE_DEFAULT;
+               break;
+       case PROC_STATE_FOREGROUND:
+               oom_score = OOMADJ_SERVICE_FOREGRD;
+               break;
+       case PROC_STATE_BACKGROUND:
+               oom_score = OOMADJ_SERVICE_BACKGRD;
+               break;
+       }
+       proc_set_oom_score_adj(pid, oom_score);
+}
+
+static pid_t get_service_pid(struct proc_process_info_t *info_t)
+{
+       GSList *iter = NULL;
+
+       if (!info_t) {
+               _D("Can't find process_info");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       gslist_for_each_item(iter, info_t->pids) {
+               struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
+
+               if (pid_info->type == RESOURCED_APP_TYPE_SERVICE) {
+                       _D("get_service_pid : pid (%d), type (%d)", pid_info->pid, pid_info->type);
+                       return pid_info->pid;
+               }
+       }
+       return RESOURCED_ERROR_NO_DATA;
+}
+
+void proc_set_process_info_memcg(struct proc_process_info_t *process_info, int memcg_idx)
+{
+       if (!process_info)
+               return;
+       process_info->memcg_idx = memcg_idx;
+}
+
+struct proc_process_info_t *find_process_info(const char *appid, const pid_t pid, const char *pkgid)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *info_t = NULL;
+
+       if (pkgid) {
+               gslist_for_each_item(iter, proc_process_list) {
+                       info_t = (struct proc_process_info_t *)iter->data;
+                       if (equal_process_info(info_t->pkgname, pkgid))
+                               return info_t;
+               }
+               return NULL;
+       }
+
+       if (!pid) {
+               gslist_for_each_item(iter, proc_process_list) {
+                       info_t = (struct proc_process_info_t *)iter->data;
+                       if (equal_process_info(info_t->appid, appid))
+                               return info_t;
+               }
+               return NULL;
+       }
+
+       gslist_for_each_item(iter, proc_process_list) {
+               info_t = (struct proc_process_info_t *)iter->data;
+               if (info_t->pids && find_pid_info(info_t->pids, pid))
+                       return info_t;
+       }
+       return NULL;
+}
+
+static resourced_ret_c proc_update_process_state(const pid_t pid, const int state)
+{
+       struct proc_process_info_t *process_info = NULL;
+       pid_t service_pid;
+       process_info = find_process_info(NULL, pid, NULL);
+       if (!process_info) {
+               _E("Current pid (%d) didn't have any process list", pid);
+               return RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+       process_info->state = state;
+       service_pid = get_service_pid(process_info);
+       if (service_pid)
+               proc_set_service_oomscore(service_pid, state);
+       return RESOURCED_ERROR_NONE;
+}
+
+resourced_ret_c proc_set_runtime_exclude_list(const int pid, int type)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *process_info = NULL;
+       struct pid_info_t *found_pid = NULL;
+
+       gslist_for_each_item(iter, proc_process_list) {
+               process_info = (struct proc_process_info_t *)iter->data;
+               if (!process_info->pids)
+                       continue;
+
+               found_pid = find_pid_info(process_info->pids, pid);
+               if(!found_pid)
+                       continue;
+
+               if(process_info->runtime_exclude) {
+                       if (type == PROC_EXCLUDE)
+                               process_info->runtime_exclude++;
+                       else
+                               process_info->runtime_exclude--;
+               } else
+                       process_info->runtime_exclude = type;
+
+               _D("found_pid %d, set proc exclude list, type = %d, exclude = %d",
+                           found_pid->pid, type, process_info->runtime_exclude);
+               break;
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+struct proc_process_info_t * proc_add_process_list(const int type, const pid_t pid, const char *appid, const char *pkgid)
+{
+       struct proc_process_info_t *process_info;
+
+       if (!appid)
+               return NULL;
+
+       process_info = find_process_info(appid, pid, pkgid);
+       /* do not add if it already in list */
+       if (process_info && find_pid_info(process_info->pids, pid))
+               return process_info;
+
+       if (!process_info) {
+               process_info = malloc(sizeof(struct proc_process_info_t));
+               if (!process_info)
+                       return NULL;
+
+               memset(process_info, 0, sizeof(struct proc_process_info_t));
+               strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
+               process_info->proc_exclude = resourced_proc_excluded(appid);
+               if (pkgid)
+                       strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
+               else
+                       extract_pkgname(process_info->appid, process_info->pkgname,
+                               MAX_NAME_LENGTH);
+               pthread_mutex_lock(&proc_mutex);
+               proc_process_list = g_slist_prepend(proc_process_list,
+                       process_info);
+               pthread_mutex_unlock(&proc_mutex);
+               process_info->state = PROC_STATE_DEFAULT;
+       }
+       if (proc_check_ug(pid) == RESOURCED_ERROR_NONFREEZABLE)
+               process_info->runtime_exclude = PROC_EXCLUDE;
+       if (type == RESOURCED_APP_TYPE_SERVICE)
+               proc_set_service_oomscore(pid, process_info->state);
+
+       proc_add_pid_list(process_info, pid, type);
+       return process_info;
+}
+
+struct proc_process_info_t * proc_create_process_list(const char *appid, const char *pkgid)
+{
+       struct proc_process_info_t *process_info;
+
+       if (!appid)
+               return NULL;
+
+       process_info = find_process_info(appid, 0, pkgid);
+       /* do not add if it already in list */
+       if (process_info)
+               return process_info;
+
+       if (!process_info) {
+               process_info = malloc(sizeof(struct proc_process_info_t));
+               if (!process_info)
+                       return NULL;
+
+               memset(process_info, 0, sizeof(struct proc_process_info_t));
+               strncpy(process_info->appid, appid, MAX_NAME_LENGTH - 1);
+               process_info->proc_exclude = resourced_proc_excluded(appid);
+               if (pkgid)
+                       strncpy(process_info->pkgname, pkgid, MAX_NAME_LENGTH - 1);
+               else
+                       extract_pkgname(process_info->appid, process_info->pkgname,
+                               MAX_NAME_LENGTH);
+               pthread_mutex_lock(&proc_mutex);
+               proc_process_list = g_slist_prepend(proc_process_list,
+                       process_info);
+               pthread_mutex_unlock(&proc_mutex);
+               process_info->state = PROC_STATE_DEFAULT;
+       }
+       return process_info;
+}
+
+int proc_remove_process_list(const pid_t pid)
+{
+       GSList *iter = NULL;
+       struct proc_process_info_t *process_info = NULL;
+       struct pid_info_t *found_pid = NULL;
+
+       pthread_mutex_lock(&proc_mutex);
+       gslist_for_each_item(iter, proc_process_list) {
+               process_info = (struct proc_process_info_t *)iter->data;
+               if (!process_info->pids)
+                       continue;
+
+               found_pid = find_pid_info(process_info->pids, pid);
+               if(!found_pid)
+                       continue;
+
+               _D("found_pid %d", found_pid->pid);
+               /* Introduce function for removing and cleaning */
+               process_info->pids = g_slist_remove(process_info->pids,
+                       found_pid);
+               free(found_pid);
+               if (!process_info->pids) {
+                       proc_process_list = g_slist_remove(
+                               proc_process_list,
+                               process_info);
+                       free(process_info);
+               }
+               break;
+       }
+       pthread_mutex_unlock(&proc_mutex);
+       return 0;
+}
+
+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 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_strdup(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 is not supported. Can not 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);
+}
+
+int resourced_proc_init(const struct daemon_opts *opts)
+{
+       int ret;
+
+       proc_notifd = proc_noti_init( );
+
+       ret = proc_monitor_init();
+       if (ret)
+               _E("proc_monitor_init failed : %d", ret);
+
+       proc_exclude_init();
+       return ret;
+}
+
+int resourced_proc_exit(const struct daemon_opts *opts)
+{
+       if (proc_notifd)
+               close(proc_notifd);
+       g_hash_table_destroy(proc_exclude_list);
+       ecore_file_monitor_del(exclude_list_monitor);
+       g_slist_free_full(proc_process_list, free);
+       return RESOURCED_ERROR_NONE;
+}
+
+void proc_set_apptype(const char *appid, const char *pkgid, int type)
+{
+       struct proc_process_info_t *process_info =
+               proc_create_process_list(appid, pkgid);
+       if (process_info)
+               process_info->type = type;
+}
+
+int resourced_proc_status_change(int type, pid_t pid, char* app_name, char* pkg_name)
+{
+       int ret = 0, oom_score_adj = 0;
+       char pidbuf[32];
+       struct proc_status proc_data;;
+
+       if (pid && (proc_get_oom_score_adj(pid, &oom_score_adj) < 0)) {
+               /* due process with pid is no longer exits
+                * we need to remove it from
+                * freezer_process_list  */
+               proc_remove_process_list(pid);
+               _E("Empty pid or process not exists. %d", pid);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if (!pid) {
+               _E("invalid pid : %d of %s", pid, app_name ? app_name : "noprocess");
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       proc_data.pid = pid;
+       proc_data.appid = app_name;
+       proc_data.processinfo = NULL;
+       switch (type) {
+       case PROC_CGROUP_SET_FOREGRD:
+               _SD("set foreground : %d", pid);
+               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;
+               proc_update_process_state(pid, PROC_STATE_FOREGROUND);
+               resourced_notify(RESOURCED_NOTIFIER_APP_FOREGRD, &proc_data);
+               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);
+               ret = resourced_proc_excluded(app_name);
+               if (!ret)
+                       proc_data.processinfo = proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
+               resourced_notify(RESOURCED_NOTIFIER_APP_LAUNCH, &proc_data);
+               _E("available memory = %u", get_available());
+               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);
+               proc_add_process_list(RESOURCED_APP_TYPE_SERVICE, pid, app_name, pkg_name);
+               if (resourced_proc_excluded(app_name) == RESOURCED_ERROR_NONE)
+                       resourced_notify(RESOURCED_NOTIFIER_SERVICE_LAUNCH, &proc_data);
+               break;
+       case PROC_CGROUP_SET_RESUME_REQUEST:
+               _SD("resume request %d", pid);
+               /* init oom_score_value */
+               if (oom_score_adj >= OOMADJ_BACKGRD_UNLOCKED) {
+                       resourced_notify(RESOURCED_NOTIFIER_APP_RESUME, &proc_data);
+                       proc_set_oom_score_adj(pid, OOMADJ_INIT);
+               }
+
+               if (!app_name) {
+                       _E("need application name!pid = %d", pid);
+                       return RESOURCED_ERROR_NO_DATA;
+               }
+
+               proc_add_process_list(RESOURCED_APP_TYPE_GUI, pid, app_name, pkg_name);
+               if (ret != RESOURCED_ERROR_NONE)
+                       _D("Failed to add to freezer list: pid %d", pid);
+
+               break;
+       case PROC_CGROUP_SET_TERMINATE_REQUEST:
+               resourced_notify(RESOURCED_NOTIFIER_APP_TERMINATE, &proc_data);
+               proc_remove_process_list(pid);
+               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, &proc_data);
+               proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+               break;
+       case PROC_CGROUP_SET_BACKGRD:
+               snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
+               dbus_proc_handler(PREDEF_BACKGRD, pidbuf);
+               ret = proc_set_backgrd(pid, oom_score_adj);
+               if (ret != 0)
+                       break;
+               proc_update_process_state(pid, PROC_STATE_BACKGROUND);
+               break;
+       case PROC_CGROUP_SET_INACTIVE:
+               ret = proc_set_inactive(pid, oom_score_adj);
+               if (ret != RESOURCED_ERROR_OK)
+                       break;
+               resourced_notify(RESOURCED_NOTIFIER_APP_INACTIVE, &proc_data);
+               break;
+       case PROC_CGROUP_GET_MEMSWEEP:
+               ret = proc_sweep_memory(PROC_SWEEP_EXCLUDE_ACTIVE, pid);
+               break;
+       case PROC_CGROUP_SET_NOTI_REQUEST:
+               break;
+       case PROC_CGROUP_SET_PROC_EXCLUDE_REQUEST:
+               proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+               break;
+       default:
+               ret = RESOURCED_ERROR_INVALID_PARAMETER;
+       }
+       return ret;
+}
+
+int resourced_proc_action(int type, 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];
+       if ((pid = atoi(pidbuf)) < 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, type %d \n", cgroup_name, pid, type);
+       return resourced_proc_status_change(type, pid, cgroup_name, pkg_name);
+}
+
diff --git a/src/proc-stat/proc-monitor.c b/src/proc-stat/proc-monitor.c
new file mode 100644 (file)
index 0000000..5ae3008
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * 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 "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 "lowmem-handler.h"
+#include "notifier.h"
+
+#include <journal/system.h>
+
+#define WATCHDOG_LAUNCHING_PARAM "WatchdogPopupLaunch"
+#define WATCHDOG_KEY1                  "_SYSPOPUP_CONTENT_"
+#define WATCHDOG_KEY2                  "_APP_NAME_"
+#define WATCHDOG_VALUE_1                       "watchdog"
+
+#define SIGNAL_PROC_WATCHDOG_RESULT    "WatchdogResult"
+#define SIGNAL_PROC_ACTIVE             "Active"
+#define SIGNAL_PROC_EXCLUDE            "ProcExclude"
+#define SIGNAL_PROC_PRELAUNCH          "ProcPrelaunch"
+#define SIGNAL_PROC_STATUS             "ProcStatus"
+#define SIGNAL_PROC_SWEEP              "ProcSweep"
+#define SIGNAL_PROC_WATCHDOG           "ProcWatchdog"
+#define SIGNAL_PROC_GROUP              "ProcGroup"
+#define TIZEN_DEBUG_MODE_FILE   "/opt/etc/.debugmode"
+
+
+#define INIT_PID       1
+#define INIT_PROC_VAL  -1
+
+static int proc_watchdog_state;
+static int proc_dbus_proc_state;
+int current_lcd_state;
+
+static Ecore_Timer *watchdog_check_timer = NULL;
+#define WATCHDOG_TIMER_INTERVAL                90
+
+/*
+ * Callback function executed by edbus 'Signal' method call. Extracts
+ * process pid and signal type from message body and uses it to send a specific
+ * signal to the process.
+ */
+static DBusMessage *edbus_signal_trigger(E_DBus_Object *obj, DBusMessage *msg);
+
+static struct proc_watchdog_info {
+       pid_t pid;
+       int signum;
+} proc_watchdog = { -1, -1 };
+
+/*
+ * Adds function callbacks to edbus interface.
+ */
+static resourced_ret_c proc_dbus_init(void);
+
+static const struct edbus_method edbus_methods[] = {
+       { "Signal", "ii", NULL, edbus_signal_trigger },
+       /* Add methods here */
+};
+
+enum proc_status_type { /** cgroup command type **/
+       PROC_STATUS_LAUNCH,
+       PROC_STATUS_RESUME,
+       PROC_STATUS_TERMINATE,
+       PROC_STATUS_FOREGRD,
+       PROC_STATUS_BACKGRD,
+};
+
+static int check_debugenable(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 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);
+}
+
+int proc_get_dbus_proc_state(void)
+{
+       return proc_dbus_proc_state;
+}
+
+static void proc_set_dbus_proc_state(int state)
+{
+       proc_dbus_proc_state = state;
+}
+
+static void proc_dbus_proc_signal_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int ret, type, convert = 0;
+       pid_t pid;
+
+       ret = dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_STATUS);
+       if (ret == 0) {
+               _D("there is no active signal");
+               return;
+       }
+
+       dbus_error_init(&err);
+
+       if (dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &type, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID) == 0) {
+               _D("there is no message");
+               return;
+       }
+
+       if (!proc_dbus_proc_state)
+               proc_set_dbus_proc_state(PROC_DBUS_ENABLE);
+
+       switch (type) {
+       case PROC_STATUS_LAUNCH:
+               convert = PROC_CGROUP_SET_LAUNCH_REQUEST;
+               break;
+       case PROC_STATUS_RESUME:
+               convert = PROC_CGROUP_SET_RESUME_REQUEST;
+               break;
+       case PROC_STATUS_TERMINATE:
+               convert = PROC_CGROUP_SET_TERMINATE_REQUEST;
+               break;
+       case PROC_STATUS_FOREGRD:
+               convert = PROC_CGROUP_SET_FOREGRD;
+               break;
+       case PROC_STATUS_BACKGRD:
+               convert = PROC_CGROUP_SET_BACKGRD;
+               break;
+       default:
+               return;
+       }
+       _D("call proc_dbus_proc_signal_handler : pid = %d, type = %d", pid, convert);
+       resourced_proc_status_change(convert, pid, NULL, NULL);
+}
+
+static void proc_dbus_exclude_signal_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int ret;
+       char *str;
+       pid_t pid;
+
+       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);
+       if (!strcmp(str, "exclude"))
+               proc_set_runtime_exclude_list(pid, PROC_EXCLUDE);
+       else if (!strcmp(str, "include"))
+               proc_set_runtime_exclude_list(pid, PROC_INCLUDE);
+       else
+               return;
+}
+
+static void proc_dbus_prelaunch_signal_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int ret;
+       char *appid;
+       char *pkgid;
+       int flags;
+
+       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_INVALID) == 0) {
+               _D("there is no message");
+               return;
+       }
+
+       _D("call proc_dbus_prelaunch_handler: appid = %s, pkgid = %s, flags = %d",
+               appid, pkgid, flags);
+
+       if (flags & PROC_LARGE_HEAP) {
+               proc_set_apptype(appid, pkgid, PROC_LARGE_HEAP);
+               lowmem_dynamic_process_killer(DYNAMIC_KILL_LARGEHEAP);
+       }
+}
+
+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)
+{
+       ecore_timer_del(watchdog_check_timer);
+       watchdog_check_timer = NULL;
+       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) {
+                       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_WATCHDOG, SYSTEM_POPUP_IFACE_WATCHDOG, 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];
+
+       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;
+       }
+
+       if (proc_watchdog.pid != INIT_PROC_VAL) {
+               _E("pid %d(%s) has already received watchdog siganl", pid, appname);
+               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);
+       journal_system_anr(appname);
+       if (watchdog_check_timer) {
+               _E("current killing watchdog process. so skipped kill %d(%s)\n", pid, appname);
+               return;
+       }
+
+       if (check_debugenable()) {
+               _E("just kill watchdog process when debug enabled pid: %d(%s)\n", pid, appname);
+               kill(pid, SIGABRT);
+               if (watchdog_check_timer == NULL) {
+                       watchdog_check_timer =
+                               ecore_timer_add(WATCHDOG_TIMER_INTERVAL, check_watchdog_cb, (void *)NULL);
+       }
+       }
+       else {
+               ret = proc_dbus_show_popup(appname);
+               if (ret < 0)
+                       _E("ERROR : request_to_launch_by_dbus()failed : %d", ret);
+               else {
+                       proc_watchdog.pid = pid;
+                       proc_watchdog.signum = command;
+               }
+       }
+}
+
+static void proc_dbus_grouping_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int pid, childpid, ret;
+
+       if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_GROUP) == 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,
+               &childpid, DBUS_TYPE_INVALID);
+
+       if (ret == 0) {
+               _D("there is no message");
+               return;
+       }
+
+       proc_set_group(pid, childpid);
+}
+
+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_LCD_ON) == 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_LCD_OFF) == 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 resourced_ret_c proc_dbus_init(void)
+{
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_WATCHDOG_RESULT,
+                   proc_dbus_watchdog_result);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_ACTIVE,
+                   proc_dbus_active_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_EXCLUDE,
+                   proc_dbus_exclude_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_PRELAUNCH,
+                   proc_dbus_prelaunch_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_STATUS,
+                   proc_dbus_proc_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_SWEEP,
+                   proc_dbus_sweep_signal_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_WATCHDOG,
+                   proc_dbus_watchdog_handler);
+
+       register_edbus_signal_handler(RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS,
+                       SIGNAL_PROC_WATCHDOG,
+                   proc_dbus_grouping_handler);
+
+       register_edbus_signal_handler(DEVICED_PATH_DISPLAY,
+                   DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_ON, proc_dbus_lcd_on);
+
+       register_edbus_signal_handler(DEVICED_PATH_DISPLAY,
+                   DEVICED_INTERFACE_DISPLAY, SIGNAL_LCD_OFF, proc_dbus_lcd_off);
+
+       /* 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));
+}
+
+resourced_ret_c proc_monitor_init(void)
+{
+       return proc_dbus_init();
+}
diff --git a/src/proc-stat/proc-noti.c b/src/proc-stat/proc-noti.c
new file mode 100644 (file)
index 0000000..bbfa6b0
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * 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 "macro.h"
+#include "proc-main.h"
+#include "proc-noti.h"
+#include "proc-process.h"
+#include "resourced.h"
+#include "trace.h"
+
+/*
+ * @desc function receives uint
+ * negative value for error reporting
+ */
+static inline int recv_int(int fd)
+{
+       int val, r = -1;
+       while (1) {
+               r = read(fd, &val, sizeof(int));
+               if (r >= 0)
+                       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) {
+               _D("str is null");
+               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
+*/
+static int read_message(int fd, struct resourced_noti *msg)
+{
+       int i;
+
+       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->path = recv_str(fd);
+       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);
+
+       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);
+}
+
+static inline void free_message(struct resourced_noti *msg)
+{
+       int i;
+
+       internal_free(msg->path);
+
+       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);
+}
+
+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;
+
+       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 = malloc(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;
+       }
+
+       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;
+       }
+
+       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;
+       struct sockaddr_un serveraddr;
+
+       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));
+
+       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;
+}
+
+int proc_noti_init()
+{
+       int fd;
+       fd = proc_noti_socket_init();
+       ecore_main_fd_handler_add(fd, ECORE_FD_READ, (Ecore_Fd_Cb)proc_noti_cb,
+                                 NULL, NULL, NULL);
+       return fd;
+}
diff --git a/src/proc-stat/proc-process.c b/src/proc-stat/proc-process.c
new file mode 100644 (file)
index 0000000..e9da6fb
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * 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 "resourced.h"
+#include "trace.h"
+#include "proc-main.h"
+#include "cgroup.h"
+#include "proc-process.h"
+#include "lowmem-common.h"
+#include "logging-common.h"
+#include "macro.h"
+#include "swap-common.h"
+#include "proc-noti.h"
+#include "notifier.h"
+
+#define PROC_OOM_SCORE_ADJ_PATH "/proc/%d/oom_score_adj"
+#define PROC_SWEEP_TIMER       3
+static GHashTable *proc_sweep_list;
+static Ecore_Timer *proc_sweep_timer = NULL;
+
+enum proc_background_type {
+       PROC_BACKGROUND_INACTIVE,
+       PROC_BACKGROUND_ACTIVE,
+};
+
+static int proc_backgrd_manage(int currentpid, int active)
+{
+       int pid = -1, pgid, ret;
+       struct proc_status proc_data;;
+       DIR *dp;
+       struct dirent dentry;
+       struct dirent *result;
+       FILE *fp;
+       char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+       char appname[PROC_NAME_MAX];
+       int count = 0;
+       int candidatepid[16] = {0,};
+       int cur_oom = -1, prev_oom=OOMADJ_BACKGRD_UNLOCKED, select_pid=0;
+       static int checkprevpid = 0;
+       unsigned long swap_args[2] = {0,};
+       unsigned long logging_args[3] = {0,};
+
+       ret = proc_get_cmdline(currentpid, appname);
+       if (ret == RESOURCED_ERROR_NONE) {
+               ret = resourced_proc_excluded(appname);
+               if (!active && !ret) {
+                       proc_data.pid = currentpid;
+                       proc_data.appid = appname;
+                       resourced_notify(RESOURCED_NOTIFIER_APP_BACKGRD, &proc_data);
+               }
+               if (ret) {
+                       _D("BACKGRD MANAGE : don't manage background application by %d", currentpid);
+                       return RESOURCED_ERROR_NONE;
+               }
+       }
+
+       dp = opendir("/proc");
+       if (!dp) {
+               _E("BACKGRD MANAGE : fail to open /proc");
+               return RESOURCED_ERROR_FAIL;
+       }
+       while (!(ret = readdir_r(dp, &dentry, &result)) && result != NULL) {
+
+               if (!isdigit(dentry.d_name[0]))
+                       continue;
+
+               pid = atoi(dentry.d_name);
+               pgid = getpgid(pid);
+               if (!pgid)
+                       continue;
+
+               snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+               fp = fopen(buf, "r+");
+               if (fp == NULL)
+                       continue;
+               if (fgets(buf, sizeof(buf), fp) == NULL) {
+                       fclose(fp);
+                       continue;
+               }
+               cur_oom = atoi(buf);
+
+               logging_args[0] = (unsigned long)pid;
+               logging_args[1] = (unsigned long)pgid;
+               logging_args[2] = (unsigned long)cur_oom;
+               logging_control(LOGGING_INSERT_PROC_LIST, logging_args);
+
+               if (currentpid != pid && currentpid == pgid) {
+                       proc_set_group(currentpid, pid);
+                       fclose(fp);
+                       continue;
+               }
+
+               if (active || checkprevpid == currentpid) {
+                       fclose(fp);
+                       continue;
+               }
+
+               if (select_pid != pid && select_pid == pgid && count < 16)
+               {
+                       _D("found candidate child pid = %d, pgid = %d", pid, pgid);
+                       candidatepid[count++] = pid;
+                       fclose(fp);
+                       continue;
+               }
+
+               swap_args[0] = (unsigned long)pid;
+               if (cur_oom > OOMADJ_BACKGRD_UNLOCKED && cur_oom > prev_oom
+                               && !swap_status(SWAP_CHECK_PID, swap_args)) {
+                       count = 0;
+                       candidatepid[count++] = pid;
+                       select_pid = pid;
+                       prev_oom = cur_oom;
+               }
+
+               if (cur_oom >= OOMADJ_APP_MAX) {
+                       fclose(fp);
+                       continue;
+               } else if (cur_oom >= OOMADJ_BACKGRD_UNLOCKED) {
+                       _D("BACKGRD : process %d set score %d (before %d)",
+                                       pid, cur_oom+OOMADJ_APP_INCREASE, cur_oom);
+                       fprintf(fp, "%d", cur_oom+OOMADJ_APP_INCREASE);
+               }
+               fclose(fp);
+       }
+
+       if (ret) {
+               _E("BACKGRD MANAGE : readdir_r on /proc directory failed");
+               closedir(dp);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       if (select_pid) {
+               _D("found candidate pid = %d, count = %d", candidatepid, count);
+               swap_args[0] = (unsigned long)candidatepid;
+               swap_args[1] = (unsigned long)count;
+               resourced_notify(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_args);
+       }
+       checkprevpid = currentpid;
+       closedir(dp);
+
+       logging_control(LOGGING_UPDATE_PROC_INFO, NULL);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int proc_foregrd_manage(int pid, int oom_score_adj)
+{
+       int ret = 0;
+       GSList *iter;
+       struct proc_process_info_t *process_info =
+               find_process_info(NULL, pid, NULL);
+
+       if (!process_info) {
+               ret = proc_set_oom_score_adj(pid, oom_score_adj);
+               return ret;
+       }
+
+       gslist_for_each_item(iter, process_info->pids) {
+               struct pid_info_t *pid_info = (struct pid_info_t *)(iter->data);
+               ret = proc_set_oom_score_adj(pid_info->pid, oom_score_adj);
+       }
+       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_BACKGRD_UNLOCKED) {
+               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;
+       DIR *dp;
+       struct dirent dentry;
+       struct dirent *result;
+       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;
+       dp = opendir("/proc");
+       if (!dp) {
+               _E("BACKGRD MANAGE : fail to open /proc");
+               return RESOURCED_ERROR_FAIL;
+       }
+       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_BACKGRD_UNLOCKED;
+       else
+               select_sweep_limit = OOMADJ_BACKGRD_LOCKED;
+
+       while (!(ret = readdir_r(dp, &dentry, &result)) && result != NULL) {
+               if (!isdigit(dentry.d_name[0]))
+                       continue;
+
+               pid = atoi(dentry.d_name);
+               if (pid == callpid)
+                       continue;
+
+               snprintf(buf, sizeof(buf), PROC_OOM_SCORE_ADJ_PATH, pid);
+               fp = fopen(buf, "r+");
+               if (fp == NULL)
+                       continue;
+               if (fgets(buf, sizeof(buf), fp) == NULL) {
+                       fclose(fp);
+                       continue;
+               }
+               cur_oom = atoi(buf);
+               if (cur_oom >= select_sweep_limit) {
+                       ret = proc_get_cmdline(pid, appname);
+                       if (ret != 0) {
+                               fclose(fp);
+                               continue;
+                       }
+                       proc_remove_process_list(pid);
+                       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 (ret) {
+               _E("BACKGRD MANAGE : readdir_r call on /proc failed");
+               closedir(dp);
+               return RESOURCED_ERROR_FAIL;
+       }
+       if (count > 0) {
+               proc_sweep_timer =
+                           ecore_timer_add(PROC_SWEEP_TIMER, proc_check_sweep_cb, (void *)proc_sweep_list);
+       }
+       closedir(dp);
+       return count;
+}
+
+
+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;
+}
+
+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)
+{
+       char buf[sizeof(PROC_OOM_SCORE_ADJ_PATH) + MAX_DEC_SIZE(int)] = {0};
+       FILE *fp;
+       unsigned long lowmem_args[2] = {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 (oom_score_adj >= OOMADJ_SU) {
+               lowmem_args[0] = (unsigned long)pid;
+               lowmem_args[1] = (unsigned long)oom_score_adj;
+               lowmem_control(LOWMEM_MOVE_CGROUP, lowmem_args);
+       }
+       return 0;
+}
+
+int proc_set_foregrd(pid_t pid, int oom_score_adj)
+{
+       int ret = 0;
+
+       switch (oom_score_adj) {
+       case OOMADJ_FOREGRD_LOCKED:
+       case OOMADJ_FOREGRD_UNLOCKED:
+       case OOMADJ_SU:
+               ret = 0;
+               break;
+       case OOMADJ_BACKGRD_LOCKED:
+               ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_LOCKED);
+               break;
+       case OOMADJ_BACKGRD_UNLOCKED:
+       case OOMADJ_INIT:
+               ret = proc_foregrd_manage(pid, OOMADJ_FOREGRD_UNLOCKED);
+               break;
+       default:
+               if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+                       ret = proc_set_oom_score_adj(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:
+               proc_backgrd_manage(pid, PROC_BACKGROUND_ACTIVE);
+               ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
+               break;
+       case OOMADJ_FOREGRD_UNLOCKED:
+               proc_backgrd_manage(pid, PROC_BACKGROUND_INACTIVE);
+               ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
+               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;
+
+       switch (oom_score_adj) {
+       case OOMADJ_FOREGRD_LOCKED:
+       case OOMADJ_BACKGRD_LOCKED:
+       case OOMADJ_SU:
+       case OOMADJ_INIT:
+               /* don't change oom value pid */
+               ret = -1;
+               break;
+       case OOMADJ_FOREGRD_UNLOCKED:
+               ret = proc_set_oom_score_adj(pid, OOMADJ_FOREGRD_LOCKED);
+               break;
+       case OOMADJ_BACKGRD_UNLOCKED:
+               ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_LOCKED);
+               break;
+       case OOMADJ_SERVICE_DEFAULT:
+       case OOMADJ_SERVICE_BACKGRD:
+               ret = proc_set_oom_score_adj(pid, OOMADJ_SERVICE_FOREGRD);
+               break;
+       default:
+               if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED)
+                       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_process_info_t * processinfo;
+       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:
+               processinfo = find_process_info(NULL, pid, NULL);
+               if (processinfo)
+                       ret = proc_set_oom_score_adj(pid, OOMADJ_BACKGRD_UNLOCKED);
+               break;
+       case OOMADJ_SERVICE_FOREGRD:
+               ret = proc_set_oom_score_adj(pid, OOMADJ_SERVICE_DEFAULT);
+               break;
+       default:
+               if (oom_score_adj > OOMADJ_BACKGRD_UNLOCKED) {
+                       ret = 0;
+               } else {
+                       ret = -1;
+               }
+               break;
+
+       }
+       return ret;
+}
+
+void proc_set_group(pid_t onwerpid, pid_t childpid)
+{
+       int oom_score_adj = 0;
+       struct proc_process_info_t *process_info =
+               find_process_info(NULL, onwerpid, NULL);
+
+       if (proc_get_oom_score_adj(onwerpid, &oom_score_adj) < 0) {
+               _D("owner pid(%d) was already terminated", onwerpid);
+               return;
+       }
+       if (process_info) {
+               proc_add_pid_list(process_info, childpid, RESOURCED_APP_TYPE_GROUP);
+               proc_set_oom_score_adj(childpid, oom_score_adj);
+       }
+}
+
+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 (!(ret = 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);
+       if (ret) {
+               _E("BACKGRD MANAGE : readdir_r call on /proc directory failed");
+               return RESOURCED_ERROR_FAIL;
+       }
+       return foundpid;
+}
diff --git a/src/proc-stat/proc-stat.c b/src/proc-stat/proc-stat.c
new file mode 100644 (file)
index 0000000..f81b441
--- /dev/null
@@ -0,0 +1,912 @@
+/*
+ * 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 "proc_stat.h"
+#include "trace.h"
+#include "proc-noti.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"
+
+#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;
+}
+
+
+static int get_mem_size(char *buffer, const char *const items[], const int items_len[], const int items_cnt, unsigned int mem_size[])
+{
+       char *p = buffer;
+       int num_found = 0;
+
+       while (*p && num_found < items_cnt) {
+               int i = 0;
+               while (i < items_cnt) {
+                       if (strncmp(p, items[i], items_len[i] - 1) == 0) {
+                               /* the format of line is like this "MemTotal:             745696 kB" */
+                               /* when it finds a item, skip the name of item */
+                               p += items_len[i];
+
+                               /* skip white space */
+                               while (*p == ' ')
+                                       ++p;
+
+                               char *num = p;
+
+                               /* go to the immediate next of numerical value */
+                               while (*p >= '0' && *p <= '9')
+                                       ++p;
+
+                               /* at this time, [num,p) is the value of item */
+
+                               /* insert null to p to parse [num,p] by atoi() */
+                               if (*p != 0) {
+                                       *p = 0;
+                                       ++p;
+
+                                       /* when it is the end of buffer, to avoid out of buffer */
+                                       if (*p == 0)
+                                               --p;
+                               }
+                               mem_size[i] = atoi(num);
+                               num_found++;
+                               break;
+                       }
+                       ++i;
+               }
+               ++p;
+       }
+
+       return num_found;
+}
+
+
+API bool proc_stat_get_total_mem_size(unsigned int *total_mem)
+{
+       int fd = 0;
+       int len = 0;
+       char *buffer = NULL;
+       const int buffer_size = 4096;
+       static const char *const items[] = { "MemTotal:" };
+       static const int items_len[] = {sizeof("MemTotal:") };
+       unsigned int mem_size[1];
+       int num_found;
+
+       assert(total_mem != NULL);
+
+       fd = open("/proc/meminfo", O_RDONLY);
+       if (fd < 0)
+               return false;
+
+       buffer = malloc(buffer_size);
+       assert(buffer != NULL);
+
+       len = read(fd, buffer, buffer_size - 1);
+       close(fd);
+
+       if (len < 0) {
+               free(buffer);
+               return false;
+       }
+
+       buffer[len] = 0;
+
+       /* to get the best search performance, the order of items should refelect the order of /proc/meminfo */
+       num_found = get_mem_size(buffer, items, items_len, 1 , mem_size);
+
+       free(buffer);
+
+       if (num_found < 1)
+               return false;
+
+       /* total_mem = "MemTotal" */
+       *total_mem = mem_size[0] / 1024;
+
+       return true;
+}
+
+
+API bool proc_stat_get_free_mem_size(unsigned int *free_mem)
+{
+       int fd = 0;
+       char *buffer = NULL;
+       const int buffer_size = 4096;
+       int len = 0;
+       static const char *const items[] = { "MemFree:", "Buffers:", "Cached:", "SwapCached:", "Shmem:" };
+       static const int items_len[] = { sizeof("MemFree:"), sizeof("Buffers:"), sizeof("Cached:"),
+                                                         sizeof("SwapCached:"), sizeof("Shmem:") };
+       static const int items_cnt = ARRAY_SIZE(items);
+       unsigned int mem_size[items_cnt];
+       int num_found;
+
+       assert(free_mem != NULL);
+
+       fd = open("/proc/meminfo", O_RDONLY);
+
+       if (fd < 0)
+               return false;
+
+       buffer = malloc(buffer_size);
+       assert(buffer != NULL);
+
+       len = read(fd, buffer, buffer_size - 1);
+       close(fd);
+
+       if (len < 0) {
+               free(buffer);
+               return false;
+       }
+
+       buffer[len] = 0;
+
+
+       /* to get the best search performance, the order of items should refelect the order of /proc/meminfo */
+       num_found = get_mem_size(buffer, items, items_len, items_cnt, mem_size);
+
+       free(buffer);
+
+       if (num_found < items_cnt)
+               return false;
+
+       /* free_mem = "MemFree" + "Buffers" + "Cached" + "SwapCache" - "Shmem" */
+       *free_mem = (mem_size[0] + mem_size[1] + mem_size[2] + mem_size[3] - mem_size[4]) / 1024;
+
+       return true;
+}
+
+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);
+
+       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);
+       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 = NULL;
+static GArray *proc_infos = NULL;
+
+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", __FUNCTION__);
+                       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;
+
+       client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (client_sockfd == -1) {
+               _E("%s: socket create failed\n", __FUNCTION__);
+               return -1;
+       }
+
+       bzero(&clientaddr, sizeof(clientaddr));
+       clientaddr.sun_family = AF_UNIX;
+       strncpy(clientaddr.sun_path, RESOURCED_SOCKET_PATH, sizeof(clientaddr.sun_path) - 1);
+       client_len = sizeof(clientaddr);
+
+       if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) <
+           0) {
+               _E("%s: connect failed\n", __FUNCTION__);
+               close(client_sockfd);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       send_int(client_sockfd, msg->pid);
+       send_int(client_sockfd, msg->type);
+       send_str(client_sockfd, msg->path);
+       send_int(client_sockfd, msg->argc);
+       for (i = 0; i < msg->argc; i++)
+               send_str(client_sockfd, msg->argv[i]);
+
+       if (sync) {
+               ret = read(client_sockfd, &result, sizeof(int));
+               if (ret < 0) {
+                       _E("%s: read failed\n", __FUNCTION__);
+                       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->path = NULL;
+
+       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;
+}
+
+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);
+}
+
diff --git a/src/resourced/init.c b/src/resourced/init.c
new file mode 100644 (file)
index 0000000..0b1dc8d
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * 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-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 struct daemon_opts opts = { 1,
+                                  1,
+                                  1,
+                                  COUNTER_UPDATE_PERIOD,
+                                  FLUSH_PERIOD,
+                                  RESOURCED_DEFAULT_STATE,
+                                  0};
+
+#define SWAP_MAX_ARG_SIZE 16
+
+static char swap_arg[SWAP_ARG_END][SWAP_MAX_ARG_SIZE] = { "swapoff",
+                                   "swapon",};
+
+static void print_root_usage()
+{
+       puts("You must be root to start it.");
+}
+
+static void print_usage()
+{
+       puts("resourced [Options]");
+       puts("       Application options:");
+       printf
+           ("-u [--update-period] - time interval for updating,"
+            " %d by default\n", opts.update_period);
+       printf
+           ("-f [--flush-period] - time interval for storing data at database,"
+            "%d by default\n", opts.flush_period);
+       printf("-s [--start-daemon] - start as daemon, %d by default\n",
+              opts.start_daemon);
+       puts("-v [--version] - program version");
+       puts("-h [--help] - application help");
+       printf("-c string [--swapcontrol=string] - control swap policy and "
+              "select sting %s, %s by default\n",
+              swap_arg[SWAP_OFF], swap_arg[SWAP_ON]);
+}
+
+static void print_version()
+{
+       printf("Version number: %d.%d.%d\n",
+               MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION);
+}
+
+static int parse_cmd(int argc, char **argv)
+{
+       const char *optstring = ":hvu:s:f:cw";
+       const struct option options[] = {
+               {"help", no_argument, 0, 'h'},
+               {"version", no_argument, 0, 'v'},
+               {"update-period", required_argument, 0, 'u'},
+               {"flush-period", required_argument, 0, 'f'},
+               {"start-daemon", required_argument, 0, 's'},
+               {"swapcontrol", required_argument, 0, 'c'},
+               {"enable-watchodg", no_argument, 0, 'w'},
+               {0, 0, 0, 0}
+       };
+       int longindex, retval, i;
+
+       while ((retval =
+               getopt_long(argc, argv, optstring, options, &longindex)) != -1)
+               switch (retval) {
+               case 'h':
+               case '?':
+                       print_usage();
+                       return RESOURCED_ERROR_FAIL;
+               case 'v':
+                       print_version();
+                       return RESOURCED_ERROR_FAIL;
+               case 'u':
+                       opts.update_period = atoi(optarg);
+                       break;
+               case 'f':
+                       opts.flush_period = atoi(optarg);
+                       break;
+               case 's':
+                       opts.start_daemon = atoi(optarg);
+                       break;
+               case 'c':
+                       for (i = 0; i < SWAP_ARG_END; i++)
+                               if (optarg && !strncmp(optarg, swap_arg[i],
+                                                      SWAP_MAX_ARG_SIZE)) {
+                                       opts.enable_swap = i;
+                                       _D("argment swaptype = %s",
+                                          swap_arg[i]);
+                                       break;
+                               }
+                       break;
+               case 'o':
+                       break;
+               case 'w':
+                       proc_set_watchdog_state(PROC_WATCHDOG_ENABLE);
+                       break;
+               default:
+                       printf("Unknown option %c\n", (char)retval);
+                       print_usage();
+                       return RESOURCED_ERROR_FAIL;
+               }
+       return RESOURCED_ERROR_NONE;
+}
+
+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)
+{
+       struct shared_modules_data *shared_data = get_shared_modules_data();
+
+       opts.state |= RESOURCED_FORCIBLY_QUIT_STATE;
+       _SD("sigterm or sigint received");
+       if (shared_data && shared_data->carg && shared_data->carg->ecore_timer) {
+               /* save data on exit, it's impossible to do in fini
+                * module function, due it executes right after ecore stopped */
+               reschedule_count_timer(shared_data->carg, 0);
+
+               /* Another way it's introduce another timer and quit main loop
+                * in it with waiting some event. */
+               sleep(TIME_TO_SAFE_DATA);
+       }
+
+       ecore_main_loop_quit();
+}
+
+static void add_signal_handler(void)
+{
+       signal(SIGTERM, sig_term_handler);
+       signal(SIGINT, sig_term_handler);
+}
+
+int resourced_init(struct daemon_arg *darg)
+{
+       int ret_code;
+
+       ret_value_msg_if(darg == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "Invalid daemon argument\n");
+       ret_code = assert_root();
+       ret_value_if(ret_code < 0, RESOURCED_ERROR_FAIL);
+       ecore_init();
+       darg->opts = &opts;
+       ret_code = parse_cmd(darg->argc, darg->argv);
+       ret_value_msg_if(ret_code < 0, RESOURCED_ERROR_FAIL,
+                        "Error parse cmd arguments\n");
+       _D("argment swaptype = %s", swap_arg[opts.enable_swap]);
+       add_signal_handler();
+       edbus_init();
+       return RESOURCED_ERROR_NONE;
+}
+
+int resourced_deinit(struct daemon_arg *darg)
+{
+       ecore_shutdown();
+       edbus_exit();
+       ret_value_msg_if(darg == NULL, RESOURCED_ERROR_INVALID_PARAMETER,
+                        "Invalid daemon argument\n");
+       return RESOURCED_ERROR_NONE;
+}
+
+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)
+               opts.state |= RESOURCED_NET_BLOCKED_STATE; /* set bit */
+       else {
+               opts.state &=(~RESOURCED_NET_BLOCKED_STATE); /* nulify bit */
+               ecore_timer_thaw(carg->ecore_timer);
+       }
+}
diff --git a/src/resourced/init.h b/src/resourced/init.h
new file mode 100644 (file)
index 0000000..000406c
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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 "resourced.h"
+
+#include "daemon-options.h"
+#include "transmission.h"
+
+struct daemon_arg {
+       int argc;
+       char **argv;
+       struct daemon_opts *opts;
+};
+
+int resourced_init(struct daemon_arg *darg);
+
+int resourced_deinit(struct daemon_arg *darg);
+
+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 */
diff --git a/src/resourced/main.c b/src/resourced/main.c
new file mode 100644 (file)
index 0000000..f25f13a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 <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 };
+       struct modules_arg marg;
+
+#ifdef 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(&marg, &darg);
+       modules_check_runtime_support(&marg);
+       modules_init(&marg);
+       ret_code = resourced_proc_init(darg.opts);
+       if (ret_code < 0)
+               _E("Proc init failed");
+       sd_notify(0, "READY=1");
+
+       ecore_main_loop_begin();
+       modules_exit(&marg);
+       resourced_deinit(&darg);
+       return ret_code;
+}
diff --git a/src/swap/swap.c b/src/swap/swap.c
new file mode 100644 (file)
index 0000000..83925cb
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * 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 "macro.h"
+#include "module.h"
+#include "module-data.h"
+#include "edbus-handler.h"
+#include "swap-common.h"
+#include "notifier.h"
+#include "proc-process.h"
+
+#include <resourced.h>
+#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>
+
+#define MAX_SWAP_VICTIMS               16
+
+#define MEMCG_PATH                     "/sys/fs/cgroup/memory"
+#define SWAPCG_PATH                    MEMCG_PATH"/swap"
+#define SWAPCG_PROCS                   SWAPCG_PATH"/cgroup.procs"
+#define SWAPCG_USAGE                   SWAPCG_PATH"/memory.usage_in_bytes"
+#define SWAPCG_RECLAIM                 SWAPCG_PATH"/memory.force_reclaim"
+
+#define SWAP_ON_EXEC_PATH              "/sbin/swapon"
+#define SWAP_OFF_EXEC_PATH             "/sbin/swapoff"
+
+#define SIGNAL_NAME_SWAP_TYPE          "SwapType"
+#define SIGNAL_NAME_SWAP_START_PID     "SwapStartPid"
+
+#define SWAPFILE_NAME                  "/dev/zram0"
+
+#define SWAP_PATH_MAX                  100
+
+#define MBtoB(x)                       (x<<20)
+#define MBtoPage(x)                    (x<<8)
+
+#define BtoMB(x)                       ((x) >> 20)
+#define BtoPAGE(x)                     ((x) >> 12)
+
+#define SWAP_TIMER_INTERVAL            0.5
+#define SWAP_PRIORITY                  20
+
+#define SWAP_COUNT_MAX                 5
+
+struct swap_data_type {
+       enum swap_status_type    status_type;
+       unsigned long *args;
+};
+
+static int swapon;
+static pthread_mutex_t swap_mutex;
+static pthread_cond_t swap_cond;
+static Ecore_Timer *swap_timer = NULL;
+
+static const struct module_ops swap_modules_ops;
+static const struct module_ops *swap_ops;
+
+pid_t swap_victims[MAX_SWAP_VICTIMS];
+
+static int swap_set_candidate_pid(void *data)
+{
+       unsigned long *args = data;
+       int i;
+       int *pid_array = (int*)args[0];
+       int count = (int)args[1];
+
+       memset(swap_victims, 0, sizeof(int)*MAX_SWAP_VICTIMS);
+
+       for (i = 0; i < count; i++)
+               swap_victims[i] = pid_array[i];
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static pid_t swap_get_candidate_pid(void)
+{
+       int i;
+       pid_t pid = 0;
+
+       for (i = 0; i < MAX_SWAP_VICTIMS; i++)
+               if(swap_victims[i]) {
+                       pid = swap_victims[i];
+                       swap_victims[i] = 0;
+                       break;
+               }
+       return pid;
+}
+
+static int swap_get_swap_type(void)
+{
+       struct shared_modules_data *modules_data = get_shared_modules_data();
+
+       ret_value_msg_if(modules_data == NULL, RESOURCED_ERROR_FAIL,
+                        "Invalid shared modules data\n");
+       return modules_data->swap_data.swaptype;
+}
+
+static void swap_set_swap_type(int type)
+{
+       struct shared_modules_data *modules_data = get_shared_modules_data();
+
+       ret_msg_if(modules_data == NULL,
+                        "Invalid shared modules data\n");
+       modules_data->swap_data.swaptype = type;
+}
+
+static int swap_check_swap_pid(int pid)
+{
+       char buf[SWAP_PATH_MAX] = {0,};
+       int swappid;
+       int ret = 0;
+       FILE *f;
+
+       f = fopen(SWAPCG_PROCS, "r");
+       if (!f) {
+               _E("%s open failed", SWAPCG_PROCS);
+               return RESOURCED_ERROR_FAIL;
+       }
+
+       while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
+               swappid = atoi(buf);
+               if (swappid == pid) {
+                       ret = swappid;
+                       break;
+               }
+       }
+       fclose(f);
+       return ret;
+}
+
+static int swap_check_swap_cgroup(void)
+{
+       char buf[SWAP_PATH_MAX] = {0,};
+       int ret = SWAP_FALSE;
+       FILE *f;
+
+       f = fopen(SWAPCG_PROCS, "r");
+       if (!f) {
+               _E("%s open failed", SWAPCG_PROCS);
+               return RESOURCED_ERROR_FAIL;
+       }
+       while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
+               ret = SWAP_TRUE;
+               break;
+       }
+       fclose(f);
+       return ret;
+}
+
+static int swap_thread_do(FILE *procs, FILE *usage_in_bytes, FILE *force_reclaim)
+{
+       char buf[SWAP_PATH_MAX] = {0,};
+       char appname[SWAP_PATH_MAX];
+       pid_t pid = 0;
+       int size;
+       int swap_cg_cnt=0;
+       unsigned long usage, nr_to_reclaim;
+
+       /* check swap cgroup count */
+       while (fgets(buf, SWAP_PATH_MAX, procs) != NULL) {
+               pid_t tpid = 0;
+               int toom = 0;
+               int ret;
+
+               if (!pid) {
+                       tpid = atoi(buf);
+
+                       if (proc_get_oom_score_adj(tpid, &toom) < 0) {
+                              _D("pid(%d) was already terminated", tpid);
+                              continue;
+                       }
+
+                       if (toom < OOMADJ_BACKGRD_UNLOCKED)
+                              continue;
+
+                       ret = proc_get_cmdline(tpid, appname);
+                       if (ret == RESOURCED_ERROR_FAIL)
+                              continue;
+
+                       pid = tpid;
+               }
+               swap_cg_cnt++;
+       }
+
+       /* swap cgroup count is MAX, kill 1 process */
+       if (swap_cg_cnt >= SWAP_COUNT_MAX) {
+               kill(pid, SIGKILL);
+               _E("we killed %d (%s)", pid, appname);
+       }
+
+       /* cacluate reclaim size by usage and swap cgroup count */
+       if (fgets(buf, 32, usage_in_bytes) == NULL)
+               return RESOURCED_ERROR_FAIL;
+
+       usage = (unsigned long)atol(buf);
+
+       nr_to_reclaim = BtoPAGE(usage) >> ((swap_cg_cnt >> 1) + 1);
+
+       /* set reclaim size */
+       size = snprintf(buf, sizeof(buf), "%lu", nr_to_reclaim);
+       if (fwrite(buf, 1, size, force_reclaim) != size)
+               _E("fwrite %s\n", buf);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static void *swap_thread_main(void * data)
+{
+       FILE *procs = NULL;
+       FILE *usage_in_bytes = NULL;
+       FILE *force_reclaim = NULL;
+
+       setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY);
+
+       if (procs == NULL) {
+               procs = fopen(SWAPCG_PROCS, "r");
+               if (procs == NULL) {
+                       _E("%s open failed", SWAPCG_PROCS);
+                       return NULL;
+               }
+       }
+
+       if (usage_in_bytes == NULL) {
+               usage_in_bytes = fopen(SWAPCG_USAGE, "r");
+               if (usage_in_bytes == NULL) {
+                       _E("%s open failed", SWAPCG_USAGE);
+                       fclose(procs);
+                       return NULL;
+               }
+       }
+
+       if (force_reclaim == NULL) {
+               force_reclaim = fopen(SWAPCG_RECLAIM, "w");
+               if (force_reclaim == NULL) {
+                       _E("%s open failed", SWAPCG_RECLAIM);
+                       fclose(procs);
+                       fclose(usage_in_bytes);
+                       return NULL;
+               }
+       }
+
+       while (1) {
+               pthread_mutex_lock(&swap_mutex);
+               pthread_cond_wait(&swap_cond, &swap_mutex);
+
+               /*
+                * when signalled by main thread, it starts
+                * swap_thread_do().
+                */
+               _I("swap thread conditional signal received");
+
+               fseek(procs, 0, SEEK_SET);
+               fseek(usage_in_bytes, 0, SEEK_SET);
+               fseek(force_reclaim, 0, SEEK_SET);
+
+               _D("swap_thread_do start");
+               swap_thread_do(procs, usage_in_bytes, force_reclaim);
+               _D("swap_thread_do end");
+               pthread_mutex_unlock(&swap_mutex);
+       }
+
+       if (procs)
+               fclose(procs);
+       if (usage_in_bytes)
+               fclose(usage_in_bytes);
+       if (force_reclaim)
+               fclose(force_reclaim);
+
+       return NULL;
+}
+
+static Eina_Bool swap_send_signal(void *data)
+{
+       int ret;
+
+       _D("swap timer callback function start");
+
+       /* signal to swap_start to start swap */
+       ret = pthread_mutex_trylock(&swap_mutex);
+
+       if (ret)
+               _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+       else {
+               pthread_cond_signal(&swap_cond);
+               _I("send signal to swap thread");
+               pthread_mutex_unlock(&swap_mutex);
+       }
+
+       _D("swap timer delete");
+
+       ecore_timer_del(swap_timer);
+       swap_timer = NULL;
+
+       return ECORE_CALLBACK_CANCEL;
+}
+
+static int swap_start(void *data)
+{
+       if (swap_timer == NULL) {
+               _D("swap timer start");
+               swap_timer =
+                       ecore_timer_add(SWAP_TIMER_INTERVAL, swap_send_signal, (void *)NULL);
+       }
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int swap_thread_create(void)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       pthread_t pth;
+
+       pthread_mutex_init(&swap_mutex, NULL);
+       pthread_cond_init(&swap_cond, NULL);
+
+       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 ret;
+}
+
+static int get_swap_status(void)
+{
+       return swapon;
+}
+
+static pid_t swap_on(void)
+{
+       pid_t pid = fork();
+
+       if (pid == 0) {
+               execl(SWAP_ON_EXEC_PATH, SWAP_ON_EXEC_PATH, "-d", SWAPFILE_NAME, (char *)NULL);
+               exit(0);
+       }
+       swapon = 1;
+       return pid;
+}
+
+static pid_t swap_off(void)
+{
+       pid_t pid = fork();
+
+       if (pid == 0) {
+               execl(SWAP_OFF_EXEC_PATH, SWAP_OFF_EXEC_PATH, SWAPFILE_NAME, (char *)NULL);
+               exit(0);
+       }
+       swapon = 0;
+       return pid;
+}
+
+static int restart_swap(void *data)
+{
+       int status;
+       pid_t pid;
+       pid_t ret;
+
+       if (!swapon) {
+               swap_on();
+               return RESOURCED_ERROR_NONE;
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               _E("fork() error");
+               return RESOURCED_ERROR_FAIL;
+       } else if (pid == 0) {
+               pid = swap_off();
+               ret = waitpid(pid, &status, 0);
+               if (ret == -1) {
+                       _E("Error waiting for swap_off child process (PID: %d, status: %d)", (int)pid, status);
+               }
+               swap_on();
+               exit(0);
+       }
+       swapon = 1;
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int swap_move_swap_cgroup(void *data)
+{
+       int *args = data;
+       int size;
+       FILE *f;
+       char buf[SWAP_PATH_MAX] = {0,};
+
+       f = fopen(SWAPCG_PROCS, "w");
+       if (!f) {
+               _E("Fail to %s file open", SWAPCG_PROCS);
+               return RESOURCED_ERROR_FAIL;
+       }
+       size = snprintf(buf, sizeof(buf), "%d", *args);
+       if (fwrite(buf, size, 1, f) != 1)
+               _E("fwrite cgroup tasks to swap cgroup failed : %d\n", *args);
+       fclose(f);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int ret;
+       pid_t pid;
+
+       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;
+       }
+
+       _I("swap cgroup entered : pid : %d", (int)pid);
+       swap_move_swap_cgroup(&pid);
+
+       if (get_swap_status() == SWAP_OFF)
+                       restart_swap(NULL);
+       swap_start(NULL);
+}
+
+static void swap_type_edbus_signal_handler(void *data, DBusMessage *msg)
+{
+       DBusError err;
+       int type;
+
+       if (dbus_message_is_signal(msg, RESOURCED_INTERFACE_SWAP, SIGNAL_NAME_SWAP_TYPE) == 0) {
+               _D("there is no swap type 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;
+       }
+
+       switch (type) {
+       case 0:
+               if (swap_get_swap_type() != SWAP_OFF) {
+                       if (swapon)
+                               swap_off();
+                       swap_set_swap_type(type);
+               }
+               break;
+       case 1:
+               if (swap_get_swap_type() != SWAP_ON) {
+                       restart_swap(NULL);
+                       swap_set_swap_type(type);
+               }
+               break;
+       default:
+               _D("It is not valid swap type : %d", type);
+               break;
+       }
+}
+
+static DBusMessage *edbus_getswaptype(E_DBus_Object *obj, DBusMessage *msg)
+{
+       DBusMessageIter iter;
+       DBusMessage *reply;
+       int state;
+
+       state = swap_get_swap_type();
+
+       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 void swap_dbus_init(void)
+{
+       resourced_ret_c ret;
+
+       register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
+                       SIGNAL_NAME_SWAP_TYPE,
+                   (void *)swap_type_edbus_signal_handler);
+       register_edbus_signal_handler(RESOURCED_PATH_SWAP, RESOURCED_INTERFACE_SWAP,
+                       SIGNAL_NAME_SWAP_START_PID,
+                   (void *)swap_start_pid_edbus_signal_handler);
+
+       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 swap_init(void)
+{
+       int ret;
+
+       ret = swap_thread_create();
+       if (ret) {
+               _E("swap thread create failed");
+               return ret;
+       }
+
+       _I("swap_init : %d", swap_get_swap_type());
+
+       swap_dbus_init();
+
+       return ret;
+}
+
+static int swap_check_node(void)
+{
+       FILE *procs = NULL;
+       FILE *usage_in_bytes = NULL;
+       FILE *force_reclaim = NULL;
+
+       procs = fopen(SWAPCG_PROCS, "r");
+       if (procs == NULL) {
+               _E("%s open failed", SWAPCG_PROCS);
+               return RESOURCED_ERROR_NO_DATA;
+       }
+
+       fclose(procs);
+
+       usage_in_bytes = fopen(SWAPCG_USAGE, "r");
+       if (usage_in_bytes == NULL) {
+               _E("%s open failed", SWAPCG_USAGE);
+               return RESOURCED_ERROR_NO_DATA;
+       }
+
+       fclose(usage_in_bytes);
+
+       force_reclaim = fopen(SWAPCG_RECLAIM, "w");
+       if (force_reclaim == NULL) {
+               _E("%s open failed", SWAPCG_RECLAIM);
+               return RESOURCED_ERROR_NO_DATA;
+       }
+
+       fclose(force_reclaim);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_swap_check_runtime_support(void *data)
+{
+       return swap_check_node();
+}
+
+static int resourced_swap_status(void *data)
+{
+       int ret = RESOURCED_ERROR_NONE;
+       struct swap_data_type *s_data;
+
+       s_data = (struct swap_data_type *)data;
+       switch(s_data->status_type) {
+       case SWAP_GET_TYPE:
+               ret = swap_get_swap_type();
+               break;
+       case SWAP_GET_CANDIDATE_PID:
+               ret = swap_get_candidate_pid();
+               break;
+       case SWAP_GET_STATUS:
+               ret = get_swap_status();
+               break;
+       case SWAP_CHECK_PID:
+               if (s_data->args)
+                       ret = swap_check_swap_pid((int)s_data->args[0]);
+               else
+                       ret = RESOURCED_ERROR_FAIL;
+               break;
+       case SWAP_CHECK_CGROUP:
+               ret = swap_check_swap_cgroup();
+               break;
+       }
+       return ret;
+}
+
+static int resourced_swap_init(void *data)
+{
+       struct modules_arg *marg = (struct modules_arg *)data;
+       struct daemon_opts *dopt = marg->opts;
+
+       swap_ops = &swap_modules_ops;
+
+       if (dopt->enable_swap)
+               swap_set_swap_type(dopt->enable_swap);
+
+       register_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid);
+       register_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start);
+       register_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap);
+       register_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup);
+
+       return swap_init();
+}
+
+static int resourced_swap_finalize(void *data)
+{
+       unregister_notifier(RESOURCED_NOTIFIER_SWAP_SET_CANDIDATE_PID, swap_set_candidate_pid);
+       unregister_notifier(RESOURCED_NOTIFIER_SWAP_START, swap_start);
+       unregister_notifier(RESOURCED_NOTIFIER_SWAP_RESTART, restart_swap);
+       unregister_notifier(RESOURCED_NOTIFIER_SWAP_MOVE_CGROUP, swap_move_swap_cgroup);
+
+       return RESOURCED_ERROR_NONE;
+}
+
+int swap_status(enum swap_status_type type, unsigned long *args)
+{
+       struct swap_data_type s_data;
+
+       if (!swap_ops)
+               return RESOURCED_ERROR_NONE;
+
+       s_data.status_type = type;
+       s_data.args = args;
+       return swap_ops->status(&s_data);
+}
+
+static const struct module_ops swap_modules_ops = {
+       .priority = MODULE_PRIORITY_NORMAL,
+       .name = "swap",
+       .init = resourced_swap_init,
+       .exit = resourced_swap_finalize,
+       .check_runtime_support = resourced_swap_check_runtime_support,
+       .status = resourced_swap_status,
+};
+
+MODULE_REGISTER(&swap_modules_ops)
diff --git a/src/timer-slack/timer-slack.c b/src/timer-slack/timer-slack.c
new file mode 100644 (file)
index 0000000..5bbb2bf
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * 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 "proc-main.h"
+#include "proc-process.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 launch_service(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, launch_service);
+       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);
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_timer_slack_finalize(void *data)
+{
+       unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH, launch_service);
+       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);
+       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)
diff --git a/src/timer-slack/timer-slack.conf b/src/timer-slack/timer-slack.conf
new file mode 100644 (file)
index 0000000..2a35051
--- /dev/null
@@ -0,0 +1,28 @@
+[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=1
+min_slack_ns=100000000
diff --git a/src/timer-slack/timer-slack.h b/src/timer-slack/timer-slack.h
new file mode 100644 (file)
index 0000000..e6a3368
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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__ */
diff --git a/src/utils/cgroup-test.c b/src/utils/cgroup-test.c
new file mode 100644 (file)
index 0000000..9224bcb
--- /dev/null
@@ -0,0 +1,45 @@
+#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;
+}
diff --git a/src/utils/database.c b/src/utils/database.c
new file mode 100644 (file)
index 0000000..b1cbbfc
--- /dev/null
@@ -0,0 +1,30 @@
+#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;
+}
diff --git a/src/utils/datausage-tool.c b/src/utils/datausage-tool.c
new file mode 100644 (file)
index 0000000..3007aad
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * 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 "resourced.h"
+#include "rd-network.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;
+};
+
+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> ");
+}
+
+static enum run_rsml_cmd parse_cmd(int argc, char **argv,
+                                 struct arg_param *param)
+{
+       const char *optstring = "hvla:e:g:uf:t:i:d::G:R:r:O:q:Q:M:s:";
+
+       const 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'},
+               {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;
+                       param->app_id = strdup(optarg);
+                       break;
+               case 'e':
+                       if (!optarg) {
+                               printf("exclude-rst option requeres an argument.");
+                               exit(EXIT_FAILURE);
+                       }
+                       cmd = RESOURCED_EXCLUDE;
+                       param->app_id = strdup(optarg);
+                       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", &param->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", &param->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,
+                               &param->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)
+                               param->app_id = strdup(optarg);
+                       break;
+               case 'G':
+                       if (!optarg) {
+                               printf("granularity option requeres an argument.");
+                               exit(EXIT_FAILURE);
+                       }
+                       if (sscanf(optarg, "%d", &param->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;
+                       param->app_id = strdup(optarg);
+                       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",
+                            &param->rcv_limit,
+                            &param->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;
+                       param->app_id = strdup(optarg);
+
+                       break;
+               case 'Q':
+                       if (!optarg) {
+                               printf("Remove quota option requeres an argument.");
+                               exit(EXIT_FAILURE);
+                       }
+                       cmd = RESOURCED_REMOVE_QUOTA;
+                       param->app_id = strdup(optarg);
+                       break;
+               case 's':
+                       if (!optarg) {
+                               printf("Restriction state requeres an argument.");
+                               exit(EXIT_FAILURE);
+                       }
+                       cmd = RESOURCED_RESTRICTION_STATE;
+                       param->app_id = strdup(optarg);
+                       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)
+{
+       /*TODO rewrite this hack*/
+       if (user_data)
+               printf("iftype %d\n", info->iftype);
+       else
+               printf("%s\n", info->app_id ? info->app_id : UNKNOWN_APP);
+
+       if (info->interval) {
+               char buf[28];
+
+               ctime_r(&info->interval->from, buf);
+               printf("%s\t", buf);
+               ctime_r(&info->interval->to, buf);
+               printf("%s\t", buf);
+       } else
+               printf("<entire interval>\t");
+
+       printf("%ld/%ld\t%ld/%ld/%u/%u/%s\n", info->foreground.cnt.incoming_bytes,
+              info->background.cnt.incoming_bytes,
+              info->foreground.cnt.outgoing_bytes,
+              info->background.cnt.outgoing_bytes,
+              info->roaming, info->hw_net_protocol_type,
+              info->ifname);
+       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\n",
+               info->app_id ? info->app_id : UNKNOWN_APP,
+              info->iftype, info->rst_state,
+              info->rcv_limit, info->send_limit, info->roaming);
+       return RESOURCED_CONTINUE;
+}
+
+const char *state_representation[] = {
+       "UNDEFINDED",
+       "ACTIVATED",
+       "EXCLUDED",
+       "REMOVED",
+};
+
+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(&param, 0, sizeof(struct arg_param));
+       cmd = parse_cmd(argc, argv, &param);
+       switch (cmd) {
+       case RESOURCED_APPLY:
+       {
+               int err = 0;
+               resourced_net_restrictions net_rst = {0,};
+
+               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;
+
+               net_rst.send_limit = param.send_limit;
+               net_rst.rcv_limit = param.rcv_limit;
+               net_rst.iftype = param.du_rule.iftype;
+
+               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(&param.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, &param.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)
+                       ret_code = remove_restriction_by_iftype(
+                               param.app_id, param.du_rule.iftype);
+               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 };
+               if (!param.du_rule.from || !param.du_rule.to) {
+                       fprintf(stderr, "Quota command requires all of this options: "
+                              "--from, --to and --roaming\n");
+                       break;
+               }
+
+               /* TODO in case of refactoring, use internal command line structure instead of public structure for holding param */
+               time_t quota_start_time = time(NULL);
+               quota.start_time = &quota_start_time;
+               quota.snd_quota = param.send_limit;
+               quota.rcv_quota = param.rcv_limit;
+               quota.iftype = param.du_rule.iftype;
+               quota.time_period = param.du_rule.to - param.du_rule.from;
+               quota.roaming_type =  param.roaming_type;
+               if (set_datausage_quota(param.app_id, &quota) !=
+                    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;
+
+               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;
+}
diff --git a/src/utils/hashtest.c b/src/utils/hashtest.c
new file mode 100644 (file)
index 0000000..e4fc481
--- /dev/null
@@ -0,0 +1,16 @@
+#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;
+}
diff --git a/src/utils/iface-test.c b/src/utils/iface-test.c
new file mode 100644 (file)
index 0000000..05f245f
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+#include "iface.h"
+
+int main(void)
+{
+       init_iftype();
+       return 0;
+}
diff --git a/src/utils/inodeuser.c b/src/utils/inodeuser.c
new file mode 100644 (file)
index 0000000..0e0159f
--- /dev/null
@@ -0,0 +1,9 @@
+#include <inode2pid.h>
+
+int main(int argc, char **argv)
+{
+       int index;
+       for (index = 0; index < 15; ++index)
+               update_inode_pid_map();
+       return 0;
+}
diff --git a/src/utils/monitored.c b/src/utils/monitored.c
new file mode 100644 (file)
index 0000000..fbe07e6
--- /dev/null
@@ -0,0 +1,11 @@
+#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;
+}
diff --git a/src/utils/portuser.c b/src/utils/portuser.c
new file mode 100644 (file)
index 0000000..2f917d9
--- /dev/null
@@ -0,0 +1,28 @@
+
+#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;
+}
diff --git a/src/utils/proc-stat-test.c b/src/utils/proc-stat-test.c
new file mode 100644 (file)
index 0000000..d24647b
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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;
+}
diff --git a/src/utils/test-net-activity.c b/src/utils/test-net-activity.c
new file mode 100644 (file)
index 0000000..1fd7811
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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 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;
+}
diff --git a/src/utils/udp-client.c b/src/utils/udp-client.c
new file mode 100644 (file)
index 0000000..74b3903
--- /dev/null
@@ -0,0 +1,42 @@
+#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;
+}
diff --git a/src/utils/udp-common.c b/src/utils/udp-common.c
new file mode 100644 (file)
index 0000000..5a5cf1b
--- /dev/null
@@ -0,0 +1,11 @@
+#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);
+}
diff --git a/src/utils/udp-common.h b/src/utils/udp-common.h
new file mode 100644 (file)
index 0000000..93961a0
--- /dev/null
@@ -0,0 +1,16 @@
+#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*/
+
diff --git a/src/utils/udp-server.c b/src/utils/udp-server.c
new file mode 100644 (file)
index 0000000..78644aa
--- /dev/null
@@ -0,0 +1,44 @@
+#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;
+}
diff --git a/src/vip-agent/vip-process.c b/src/vip-agent/vip-process.c
new file mode 100644 (file)
index 0000000..8686b0f
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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 "proc-main.h"
+#include "cgroup.h"
+#include "proc-process.h"
+#include "macro.h"
+#include "config-parser.h"
+#include "file-helper.h"
+#include "module.h"
+#include "module-data.h"
+#include "vip-process.h"
+
+#define VIP_CONF_FILE          "/etc/resourced/vip-process.conf"
+#define VIP_CONF_SECTION       "VIP_PROCESS"
+#define VIP_CONF_NAME          "VIP_PROC_NAME"
+#define AGENT_PATH             "/usr/bin/vip-release-agent"
+#define VIP_RELEASE_AGENT      "/release_agent"
+#define VIP_NOTIFY_ON_RELEASE  "/notify_on_release"
+
+
+static int load_vip_config(struct parse_result *result, void *user_data)
+{
+       pid_t pid = 0;
+       char cgroup_name[MAX_NAME_LENGTH];
+       if (!result)
+               return -EINVAL;
+
+       if (strcmp(result->section, VIP_CONF_SECTION))
+               return RESOURCED_ERROR_NONE;
+
+       if (!strcmp(result->name, VIP_CONF_NAME)) {
+               /* 1. find pid */
+               /* 2. decrease oom score adj for excepting it in oom candidate list */
+               /* 3. make cgroup */
+               pid =  find_pid_from_cmdline(result->value);
+               if (pid) {
+                       make_cgroup_subdir(VIP_CGROUP, result->value, NULL);
+                       snprintf(cgroup_name, sizeof(cgroup_name), "%s/%s",
+                                   VIP_CGROUP, result->value);
+                       cgroup_write_node(cgroup_name, TASK_FILE_NAME, pid);
+               }
+       }
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_vip_process_init(void *data)
+{
+       int checkfd;
+       checkfd = open(CHECK_RELEASE_PROGRESS, O_RDONLY, 0666);
+       if (checkfd >= 0)
+       {
+               if (unlink(CHECK_RELEASE_PROGRESS) < 0)
+                       _E("fail to remove %s file\n", CHECK_RELEASE_PROGRESS);
+               close(checkfd);
+       }
+       make_cgroup_subdir(DEFAULT_CGROUP, "vip", NULL);
+       mount_cgroup_subsystem("vip_cgroup", VIP_CGROUP, "none,name=vip_cgroup");
+       cgroup_write_node_str(VIP_CGROUP, VIP_RELEASE_AGENT, AGENT_PATH);
+       cgroup_write_node(VIP_CGROUP, VIP_NOTIFY_ON_RELEASE , 1);
+       config_parse(VIP_CONF_FILE, load_vip_config, NULL);
+       return RESOURCED_ERROR_NONE;
+}
+
+static int resourced_vip_process_finalize(void *data)
+{
+       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)
diff --git a/src/vip-agent/vip-process.conf b/src/vip-agent/vip-process.conf
new file mode 100644 (file)
index 0000000..b4c7572
--- /dev/null
@@ -0,0 +1,7 @@
+[VIP_PROCESS]
+# VIP process list
+VIP_PROC_NAME=Xorg
+VIP_PROC_NAME=enlightenment
+VIP_PROC_NAME=dbus-daemon
+VIP_PROC_NAME=amd
+VIP_PROC_NAME=sensord
diff --git a/src/vip-agent/vip-process.h b/src/vip-agent/vip-process.h
new file mode 100644 (file)
index 0000000..e47ad74
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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__ */
+
diff --git a/src/vip-agent/vip-release-agent.c b/src/vip-agent/vip-release-agent.c
new file mode 100644 (file)
index 0000000..12872be
--- /dev/null
@@ -0,0 +1,108 @@
+#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"
+
+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;
+       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) {
+                       strerror_r(errno, buf, sizeof(buf));
+                       _E("Error execv: %s.\n", buf);
+                       _exit(-1);
+               }
+               _exit(1);
+       }
+       do {
+               if (waitpid(pid, &status, 0) == -1) {
+                       if (errno != EINTR)
+                               return -1;
+               }
+               else {
+                       return status;
+               }
+       } while (1);
+}
+
+int main(int argc, char *argv[])
+{
+       int checkfd;
+       char *dumpargv[3] = {DUMP_PATH, NULL, 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;
+}
+
diff --git a/system-resource.manifest b/system-resource.manifest
new file mode 100644 (file)
index 0000000..3256181
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+<request>
+       <domain name="_"/>
+</request>
+</manifest>
diff --git a/system-resource.pc.in b/system-resource.pc.in
new file mode 100644 (file)
index 0000000..a610592
--- /dev/null
@@ -0,0 +1,16 @@
+# Package Information for pkg-config
+#
+# Copyright (c) 2012 Samsung Electronics Co., Ltd.
+# All rights reserved.
+#
+
+libdir=@LIBDIR@
+includedir=@INCLUDEDIR@
+
+Name: @PC_NAME_DEPRECATED@
+Description: @PACKAGE_DESCRIPTION@
+Version: @VERSION@
+Requires: @PC_REQUIRED@
+Libs: -L${libdir} @PC_PROVIDED_LIBS@
+Cflags: @PC_CFLAGS@
+