SET(COMMON_SOURCE_DIR ${SOURCE_DIR}/common)
SET(CPU_SOURCE_DIR ${SOURCE_DIR}/cpu)
SET(VIP_SOURCE_DIR ${SOURCE_DIR}/vip-agent)
- SET(TIMER_SOURCE_DIR ${SOURCE_DIR}/timer-slack)
+ SET(TIMER_SOURCE_DIR ${SOURCE_DIR}/timer-slack)
+ SET(BLOCK_SOURCE_DIR ${SOURCE_DIR}/block)
+ SET(RESOURCED_DBUS_SOURCE_DIR ${SOURCE_DIR}/resourced-dbus)
-INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION lib/pkgconfig)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system.d)
-INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.rule DESTINATION /etc/smack/accesses2.d)
- FILE(MAKE_DIRECTORY ${RESOURCED}-cmake)
- CONFIGURE_FILE(${CMAKELISTS_DIR}/${RESOURCED}.txt ${RESOURCED}-cmake/CMakeLists.txt COPYONLY)
-
-
- 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})
+ ADD_SUBDIRECTORY(src)
popup
testmode
wakeup-service
+ admin-data
+w-home
Group: System/Libraries
License: Apache-2.0
Source0: %{name}-%{version}.tar.gz
+ Source2: resourced-cpucgroup.service
- %define powertop_state OFF
%define cpu_module ON
- %define timer_slack OFF
-
%define vip_agent_module ON
+ %define timer_slack OFF
- %define logging_module ON
- %define logging_memory OFF
- %define logging_cpu ON
+ %define heart_module ON
%define memory_module ON
- %define swap_module OFF
- %define memory_vmpressure ON
-
+ %define block_module ON
+ %define wearable_noti OFF
%define network_state OFF
- %if "%{?tizen_profile_name}" == "mobile"
- %define swap_module OFF
- %define memory_vmpressure OFF
- %define network_state OFF
- %define tethering_feature OFF
- %endif
+ %define memory_eng ON
+
++%define swap_module ON
++%define freezer_module OFF
+%define tethering_feature OFF
++%define telephony_feature OFF
+
+ %define slp_tests OFF
- %if "%{?tizen_profile_name}" == "wearable"
-%if "%{?tizen_profile_name}" == "mobile"
++%if "%{?profile}" == "mobile"
%define swap_module ON
- %define memory_vmpressure ON
+ %define freezer_module ON
%define network_state OFF
+ %define tethering_feature OFF
+ %define wearable_noti OFF
+ %define telephony_feature OFF
%endif
- %if "%{?tizen_profile_name}" == "tv"
-%if "%{?tizen_profile_name}" == "wearable"
++%if "%{?profile}" == "wearable"
+ %define freezer_module ON
%define swap_module ON
- %define memory_vmpressure ON
%define network_state OFF
%define tethering_feature OFF
+ %define wearable_noti ON
+ %define telephony_feature OFF
%endif
- %if 0%{?tizen_build_binary_release_type_eng}
- %define memory_eng ON
- %else
- %define memory_eng OFF
-%if "%{?tizen_profile_name}" == "tv"
++%if "%{?profile}" == "tv"
+ %define freezer_module OFF
+ %define swap_module OFF
+ %define network_state OFF
+ %define tethering_feature OFF
+ %define wearable_noti OFF
+ %define telephony_feature OFF
%endif
%define exclude_list_file_name resourced_proc_exclude.ini
export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE"
export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
- %cmake . \
- -DFULLVER=%{version} \
- -DMAJORVER=${MAJORVER} \
- -DCMAKE_BUILD_TYPE=Release \
- -DEXCLUDE_LIST_FILE_NAME=%{exclude_list_file_name} \
- -DEXCLUDE_LIST_FULL_PATH=%{exclude_list_full_path} \
- -DEXCLUDE_LIST_OPT_FULL_PATH=%{exclude_list_opt_full_path} \
- -DVIP_AGENT=%{vip_agent_module} \
- -DSWAP_MODULE=%{swap_module} \
- -DMEMORY_ENG=%{memory_eng} \
- -DMEMORY_VMPRESSURE=%{memory_vmpressure} \
- -DMEMORY_MODULE=%{memory_module} \
- -DMEMORY_VMPRESSURE=%{memory_vmpressure} \
- -DDATABASE_FULL_PATH=%{database_full_path} \
- -DNETWORK_MODULE=%{network_state} \
- -DTELEPHONY_FEATURE=%{telephony_feature} \
- -DDATAUSAGE_TYPE=NFACCT \
- -DLOGGING_MODULE=%{logging_module} \
- -DLOGGING_MEMORY=%{logging_memory} \
- -DLOGGING_CPU=%{logging_cpu} \
- -DPOWERTOP_MODULE=%{powertop_state} \
- -DCPU_MODULE=%{cpu_module} \
- -DTIMER_SLACK=%{timer_slack}
+ %ifarch %{arm}
-%define ARCH arm
++ %define ARCH armv7l
+ %else
-%define ARCH i586
++ %ifarch aarch64
++ %define ARCH arm64
++ %else
++ %define ARCH i586
++ %endif
+ %endif
+
-%cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} \
- -DFULLVER=%{version} \
++%cmake . -DFULLVER=%{version} \
+ -DMAJORVER=${MAJORVER} \
+ -DARCH=%{ARCH} \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DEXCLUDE_LIST_FILE_NAME=%{exclude_list_file_name} \
+ -DEXCLUDE_LIST_FULL_PATH=%{exclude_list_full_path} \
+ -DDATABASE_FULL_PATH=%{database_full_path} \
+ -DEXCLUDE_LIST_OPT_FULL_PATH=%{exclude_list_opt_full_path} \
+ -DNETWORK_MODULE=%{network_state} \
+ -DSWAP_MODULE=%{swap_module} \
+ -DFREEZER_MODULE=%{freezer_module} \
+ -DCPU_MODULE=%{cpu_module} \
+ -DMEMORY_ENG=%{memory_eng} \
+ -DVIP_AGENT=%{vip_agent_module} \
+ -DTELEPHONY_FEATURE=%{telephony_feature} \
+ -DTIMER_SLACK=%{timer_slack} \
+ -DHEART_MODULE=%{heart_module} \
+ -DDATAUSAGE_TYPE=NFACCT \
+ -DMEMORY_MODULE=%{memory_module} \
+ -DWEARABLE_NOTI=%{wearable_noti} \
+ -DBLOCK_MODULE=%{block_module} \
+ -DSLP_TESTS=%{slp_tests}
make %{?jobs:-j%jobs}
mkdir -p %{buildroot}/opt/usr/dbspace
sqlite3 %{buildroot}%{database_full_path} < %{buildroot}/usr/share/traffic_db.sql
rm %{buildroot}/usr/share/traffic_db.sql
+ sqlite3 %{buildroot}%{database_full_path} < %{buildroot}/usr/share/exception_db.sql
+ rm %{buildroot}/usr/share/exception_db.sql
%endif
--mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants
- #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
+ %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
++ %install_service graphical.target.wants resourced-cpucgroup.service
%endif
%pre resourced
systemctl stop resourced.service
fi
- %post -p /sbin/ldconfig
+ %post
- %post resourced
+ /sbin/ldconfig
- init_vconf()
- {
- vconftool set -t bool db/private/resourced/wifi_statistics 1 -i -f -s tizen::vconf::platform::rw
- vconftool set -t bool db/private/resourced/datacall 1 -i -f -s tizen::vconf::platform::rw
- vconftool set -t bool db/private/resourced/datacall_logging 1 -i -f -s tizen::vconf::platform::rw
- vconftool set -t int db/private/resourced/datausage_timer 60 -i -f -s tizen::vconf::platform::rw
- vconftool set -t string db/private/resourced/new_limit "" -u 5000 -f -s tizen::vconf::platform::rw
- vconftool set -t string db/private/resourced/delete_limit "" -u 5000 -f -s tizen::vconf::platform::rw
- vconftool set -t int db/private/resourced/network_db_entries 0 -i -f -s tizen::vconf::platform::rw
- }
+ mkdir -p %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
-ln -sf %{_libdir}/systemd/system/resourced.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
++ln -sf %{_unitdir}/resourced.service %{_sysconfdir}/systemd/default-extra-dependencies/ignore-units.d/
- %if %{?network_state} == ON
- init_vconf
- %endif
#install init.d script
mkdir -p /opt/usr/etc
#make empty dynamic exclude list for first installation
systemctl start resourced.service
fi
+%postun -p /sbin/ldconfig
+
%files -n resourced
/usr/share/license/%{name}
-/etc/smack/accesses2.d/resourced.rule
%attr(-,root, root) %{_bindir}/resourced
+ %manifest resourced.manifest
%if %{?network_state} == ON
%config(noreplace) %attr(660,root,app) %{database_full_path}
%config(noreplace) %attr(660,root,app) %{database_full_path}-journal
%config /etc/resourced/network.conf
/etc/opt/upgrade/500.resourced-datausage.patch.sh
%attr(700,root,root) /etc/opt/upgrade/500.resourced-datausage.patch.sh
+ %{_bindir}/net-cls-release
%endif
-
- %manifest resourced.manifest
%config %{_sysconfdir}/dbus-1/system.d/resourced.conf
-%{_libdir}/systemd/system/resourced.service
-%{_libdir}/systemd/system/multi-user.target.wants/resourced.service
-%{_libdir}/systemd/system/resourced.socket
-%{_libdir}/systemd/system/sockets.target.wants/resourced.socket
+%{_unitdir}/resourced.service
+%{_unitdir}/multi-user.target.wants/resourced.service
++%{_unitdir}/resourced.socket
++%{_unitdir}/sockets.target.wants/resourced.socket
%config /etc/resourced/memory.conf
- %config /etc/resourced/cpu.conf
+ %config /etc/resourced/proc.conf
+ %if %{?cpu_module} == ON
+ %config /etc/resourced/cpu.conf
+ %else
+ %{_bindir}/resourced-cpucgroup.sh
- %{_libdir}/systemd/system/resourced-cpucgroup.service
- %{_libdir}/systemd/system/graphical.target.wants/resourced-cpucgroup.service
++ %{_unitdir}/resourced-cpucgroup.service
++ %{_unitdir}/graphical.target.wants/resourced-cpucgroup.service
+ %endif
%if %{?swap_module} == ON
- %config /etc/resourced/swap.conf
- %{_unitdir}/resourced-zram.service
- %{_unitdir}/graphical.target.wants/resourced-zram.service
- %{_bindir}/resourced-zram.sh
+ %config /etc/resourced/swap.conf
%endif
%if %{?vip_agent_module} == ON
- %config /etc/resourced/vip-process.conf
- %attr(-,root, root) %{_bindir}/vip-release-agent
+ %config /etc/resourced/vip-process.conf
+ %attr(-,root, root) %{_bindir}/vip-release-agent
%endif
%if %{?timer_slack} == ON
- %config /etc/resourced/timer-slack.conf
+ %config /etc/resourced/timer-slack.conf
+ %endif
+ %if %{?block_module} == ON
+ %config /etc/resourced/block.conf
+ %endif
+ %if %{?freezer_module} == ON
+ %{_libdir}/libsystem-freezer.so*
+ %config /etc/resourced/freezer.conf
%endif
%{exclude_list_full_path}
- %if %{?powertop_state} == ON
- /usr/share/powertop-wrapper/header.html
+ %if %{?heart_module} == ON
+ %config /etc/resourced/heart.conf
+ %attr(700, root, root) /opt/etc/dump.d/module.d/dump_heart_data.sh
+ %attr(700, app, app) %{logging_storage_db_full_path}
+ %attr(700, app, app) %{logging_storage_db_full_path}-shm
+ %attr(700, app, app) %{logging_storage_db_full_path}-wal
+ %endif
+ %if %{?slp_tests} == ON
+ /usr/bin/resourced-test
+ /usr/lib/systemd/system/resourced-test.service
+ /usr/share/dbus-1/system-services/org.tizen.system.resourced-test.service
%endif
-
#memps
- %attr(-,root, root) %{_bindir}/memps
+ %attr(-,root, system) %{_bindir}/memps
+ #mem-stress
+ %attr(-,root, root) %{_bindir}/mem-stress
-%{_libdir}/systemd/system/mem-stress.service
-%{_libdir}/systemd/system/graphical.target.wants/mem-stress.service
++%{_unitdir}/mem-stress.service
++%{_unitdir}/graphical.target.wants/mem-stress.service
%files -n libresourced
%manifest libresourced.manifest
--- /dev/null
-SET(SOURCES ${SOURCES}
- ${RESOURCED_DBUS_SOURCE_DIR}/resourced-dbus.c
- )
+ CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
+
+ #CMake doesnt' support same project name for several TARGETS
+ # libresourced uses it
+ PROJECT(resource_d)
+
+ INCLUDE(${CMAKE_SOURCE_DIR}/cmake/InstallSymlink.cmake)
+ INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ProcessGPERF.cmake)
+
+ PROCESS_GPERF(${COMMON_SOURCE_DIR}/smaps-lookup.gperf ${COMMON_SOURCE_DIR}/smaps-lookup.c)
+ PROCESS_GPERF(${COMMON_SOURCE_DIR}/meminfo-lookup.gperf ${COMMON_SOURCE_DIR}/meminfo-lookup.c)
+ SET(RESOURCED_SHARED_SOURCES ${RESOURCED_SHARED_SOURCES}
+ ${COMMON_SOURCE_DIR}/smaps-lookup.c
+ ${COMMON_SOURCE_DIR}/meminfo-lookup.c
+ )
+
+ SET(CMAKE_EXTRA_INCLUDE_FILES unistd.h)
+
+ INCLUDE_DIRECTORIES(${RESOURCED_INCLUDEDIR}
+ ${RESOURCED_SOURCE_DIR}
+ ${HEART_SOURCE_DIR}/include
+ ${MEMORY_SOURCE_DIR}
+ ${PROC-STAT_SOURCE_DIR}/include
+ ${APP-STAT_SOURCE_DIR}/include
+ ${NETWORK_SOURCE_DIR}/include
+ ${BLOCK_SOURCE_DIR}/include
+ ${RESOURCED_DBUS_SOURCE_DIR}/include
+ ${FREEZER_SOURCE_DIR}/include
+ )
+
+ SET(SOURCES
+ ${PROC-STAT_SOURCE_DIR}/proc-handler.c
+ ${PROC-STAT_SOURCE_DIR}/proc-main.c
+ ${PROC-STAT_SOURCE_DIR}/proc-noti.c
+ ${PROC-STAT_SOURCE_DIR}/proc-process.c
+ ${PROC-STAT_SOURCE_DIR}/proc-monitor.c
+ ${PROC-STAT_SOURCE_DIR}/proc-usage-stats.c
+ ${PROC-STAT_SOURCE_DIR}/proc-usage-stats-helper.c
+ ${PROC-STAT_SOURCE_DIR}/proc-appusage.c
+ ${PROC-STAT_SOURCE_DIR}/proc-info.c
+ ${SLUGGISH_SOURCE_DIR}/sluggish.c
+ ${RESOURCED_SOURCE_DIR}/init.c
+ ${RESOURCED_SOURCE_DIR}/main.c
+ ${COMMON_SOURCE_DIR}/procfs.c
+ )
+
+ INSTALL(FILES ${DATA_DIR}/${EXCLUDE_LIST_FILE_NAME} DESTINATION /usr/etc)
+ #network module
+ IF("${NETWORK_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/counter.c
+ )
+ IF("${DATAUSAGE_TYPE}" STREQUAL "NFACCT")
+ SET(CONFIG_DATAUSAGE_NFACCT 1)
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/nfacct-rule.c
+ ${NETWORK_SOURCE_DIR}/nf-restriction.c
+ )
+ ELSE()
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/generic-netlink.c
+ ${NETWORK_SOURCE_DIR}/ktgrabber-restriction.c
+ ${NETWORK_SOURCE_DIR}/ktgrabber-parser.c
+ )
+ ENDIF()
+
+ IF("${WEARABLE_NOTI}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/notification-wearable.c
+ )
+ ELSE()
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/notification-mobile.c
+ )
+ ENDIF()
+
+ SET(SOURCES ${SOURCES}
+ ${NETWORK_SOURCE_DIR}/counter-process.c
+ ${NETWORK_SOURCE_DIR}/options-private.c
+ ${NETWORK_SOURCE_DIR}/datausage-quota-processing.c
+ ${NETWORK_SOURCE_DIR}/datausage-vconf-callbacks.c
+ ${NETWORK_SOURCE_DIR}/datausage-vconf-common.c
+ ${NETWORK_SOURCE_DIR}/specific-trace.c
+ ${NETWORK_SOURCE_DIR}/datausage-common.c
+ ${NETWORK_SOURCE_DIR}/iface-cb.c
+ ${NETWORK_SOURCE_DIR}/nl-helper.c
+ ${NETWORK_SOURCE_DIR}/restriction-handler.c
+ ${NETWORK_SOURCE_DIR}/restriction-helper.c
+ ${NETWORK_SOURCE_DIR}/restriction-local.c
+ ${NETWORK_SOURCE_DIR}/tethering-restriction.c
+ ${NETWORK_SOURCE_DIR}/db-guard.c
+ )
+
+ INSTALL(FILES ${NETWORK_SOURCE_DIR}/network.conf
+ DESTINATION /etc/resourced)
+ INSTALL(FILES ${NETWORK_SOURCE_DIR}/500.resourced-datausage.patch.sh
+ DESTINATION /etc/opt/upgrade)
+
+ SET(REQUIRES_LIST ${REQUIRES_LIST}
+ openssl
+ tapi
+ )
+
+
+ ENDIF()
+
+
+ #freezer module
+ IF("${FREEZER_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES} ${FREEZER_SOURCE_DIR}/freezer.c)
++ ADD_DEFINITIONS("-DSYSTEM_LIB_PATH=\"${LIB_INSTALL_DIR}\"")
+ ELSE()
+ SET(SOURCES ${SOURCES} ${FREEZER_SOURCE_DIR}/freezer-common.c)
+ ENDIF()
+
+ #resourced DBus module
- INSTALL(FILES ${FREEZER_SOURCE_DIR}/libsystem-freezer.so.${ARCH} DESTINATION lib
++#SET(SOURCES ${SOURCES}
++# ${RESOURCED_DBUS_SOURCE_DIR}/resourced-dbus.c
++# )
+
+ IF("${VIP_AGENT}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${VIP_SOURCE_DIR}/vip-process.c
+ )
+ ENDIF()
+
+ #heart module
+ IF("${HEART_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${HEART_SOURCE_DIR}/logging.c
+ ${HEART_SOURCE_DIR}/heart.c
+ ${HEART_SOURCE_DIR}/heart-abnormal.c
+ ${HEART_SOURCE_DIR}/heart-appopt.c
+ ${HEART_SOURCE_DIR}/heart-cpu.c
+ ${HEART_SOURCE_DIR}/heart-memory.c
+ ${HEART_SOURCE_DIR}/heart-battery.c
+ ${HEART_SOURCE_DIR}/heart-storage.c
+ ${HEART_SOURCE_DIR}/decision.c
+ ${HEART_SOURCE_DIR}/decision-memory.c
+ )
+ ADD_DEFINITIONS("-DHEART_SUPPORT")
+ ENDIF()
+
+ #memory module
+ IF("${MEMORY_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${MEMORY_SOURCE_DIR}/lowmem-dbus.c
+ ${MEMORY_SOURCE_DIR}/memcontrol.c
+ ${MEMORY_SOURCE_DIR}/vmpressure-lowmem-handler.c
+ )
+ ADD_DEFINITIONS("-DMEMORY_SUPPORT")
+ ENDIF()
+
+ #swap module
+ IF("${SWAP_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${SWAP_SOURCE_DIR}/swap.c
+ )
+ ADD_DEFINITIONS("-DSWAP_SUPPORT")
+ ENDIF()
+
+ IF("${CPU_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${CPU_SOURCE_DIR}/cpu.c
+ )
+ ENDIF()
+
+ IF("${TIMER_SLACK}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${TIMER_SOURCE_DIR}/timer-slack.c
+ )
+ ENDIF()
+
+ IF("${BLOCK_MODULE}" STREQUAL "ON")
+ SET(SOURCES ${SOURCES}
+ ${BLOCK_SOURCE_DIR}/block.c
+ ${BLOCK_SOURCE_DIR}/block-monitor.c
+ )
+ ENDIF()
+
+ SET(REQUIRES_LIST ${REQUIRES_LIST}
+ ecore
+ dlog
+ glib-2.0
+ sqlite3
+ vconf
+ vconf-internal-keys
+ ecore-file
+ eina
+ edbus
+ eina
+ libsystemd
+ leveldb
+ eventsystem
+ )
+
+ INCLUDE(FindPkgConfig)
+ PKG_CHECK_MODULES(RESOURCED_REQUIRE_PKGS REQUIRED ${REQUIRES_LIST})
+
+ FOREACH(flag ${RESOURCED_REQUIRE_PKGS_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ ENDFOREACH(flag)
+
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -pthread -fPIE")
+
+ CONFIGURE_FILE(${INCLUDE_COMMON_DIR}/config.h.in
+ ${INCLUDE_COMMON_DIR}/config.h)
+
+ # resourced shared static lib
+ FILE(GLOB FILES "${INCLUDE_COMMON_DIR}/*.h")
+ FOREACH(FILE ${FILES})
+ SET(RESOURCED_INCLUDE_HEADERS ${RESOURCED_INCLUDE_HEADERS} ${FILE})
+ ENDFOREACH()
+
+ SET(RESOURCED_INCLUDE_HEADERS ${RESOURCED_INCLUDE_HEADERS}
+ ${NETWORK_SOURCE_DIR}/include/datausage-vconf-callbacks.h
+ ${NETWORK_SOURCE_DIR}/include/iface-cb.h
+ )
+
+ FILE(GLOB FILES "${COMMON_SOURCE_DIR}/*.c")
+ FOREACH(FILE ${FILES})
+ SET(RESOURCED_SHARED_SOURCES ${RESOURCED_SHARED_SOURCES} ${FILE})
+ ENDFOREACH()
+
+ FILE(GLOB FILES "${COMMON_SOURCE_DIR}/*.h")
+ FOREACH(FILE ${FILES})
+ SET(RESOURCED_SHARED_HEADERS ${RESOURCED_SHARED_HEADERS} ${FILE})
+ ENDFOREACH()
+
+ ADD_LIBRARY(resourced_shared STATIC
+ ${RESOURCED_INCLUDE_HEADERS}
+ ${RESOURCED_SHARED_SOURCES}
+ ${RESOURCED_SHARED_HEADERS}
+ )
+ TARGET_LINK_LIBRARIES(resourced_shared ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+
+ ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+ TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+ resourced_shared ${RESOURCED_REQUIRE_PKGS_LDFLAGS} "-pie -lrt -lm -ldl"
+ )
+
+ INSTALL(FILES
+ ${RESOURCED_SOURCE_DIR}/${RESOURCED}.socket
+ ${RESOURCED_SOURCE_DIR}/${RESOURCED}.service
+ DESTINATION lib/systemd/system)
+ INSTALL_SYMLINK(../${RESOURCED}.socket lib/systemd/system/sockets.target.wants/${RESOURCED}.socket)
+ INSTALL_SYMLINK(../${RESOURCED}.service lib/systemd/system/multi-user.target.wants/${RESOURCED}.service)
+
+ IF("${NETWORK_MODULE}" STREQUAL "ON")
+ TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+ resourced
+ app-stat
+ storage
+ settings
+ net-cls
+ telephony
+ net-iface
+ )
+
+ IF("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
+ ADD_EXECUTABLE(test-udp-server ${UTILS_SOURCE_DIR}/udp-server.c ${UTILS_SOURCE_DIR}/udp-common.c)
+ ADD_EXECUTABLE(test-udp-client ${UTILS_SOURCE_DIR}/udp-client.c ${UTILS_SOURCE_DIR}/udp-common.c)
+ ADD_EXECUTABLE(iface-test ${UTILS_SOURCE_DIR}/iface-test.c)
+ TARGET_LINK_LIBRARIES(iface-test net-iface ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+ ENDIF()
+
+ INSTALL(FILES ${DATA_DIR}/traffic_db.sql
+ DESTINATION /usr/share)
+ INSTALL(FILES ${DATA_DIR}/exception_db.sql
+ DESTINATION /usr/share)
+ ENDIF()
+
+ INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/resourced.h DESTINATION include/system)
+
+ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SKIP_BUILD_RPATH true)
+ INSTALL(FILES ${PROJECT_NAME}
+ DESTINATION ${MAKE_INSTALL_PREFIX}/usr/bin RENAME resourced
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE WORLD_EXECUTE)
+
+ IF("${MEMORY_ENG}" STREQUAL "ON")
+ INSTALL(FILES ${MEMORY_SOURCE_DIR}/memory_eng.conf
+ DESTINATION /etc/resourced RENAME memory.conf)
+ ELSE()
+ INSTALL(FILES ${MEMORY_SOURCE_DIR}/memory_user.conf
+ DESTINATION /etc/resourced RENAME memory.conf)
+ ENDIF()
+
+ IF("${SWAP_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${SWAP_SOURCE_DIR}/swap.conf DESTINATION /etc/resourced)
+ ENDIF()
+
+ IF("${HEART_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${HEART_SOURCE_DIR}/heart.conf
+ DESTINATION /etc/resourced RENAME heart.conf)
+ INSTALL(FILES ${HEART_SOURCE_DIR}/dump_heart_data.sh
+ DESTINATION /opt/etc/dump.d/module.d)
+ ENDIF()
+
+ IF("${CPU_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${CPU_SOURCE_DIR}/cpu.conf
+ DESTINATION /etc/resourced RENAME cpu.conf)
+ ELSE()
+ INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/scripts/resourced-cpucgroup.sh DESTINATION bin)
+ ENDIF()
+
+ IF("${TIMER_SLACK}" STREQUAL "ON")
+ INSTALL(FILES ${TIMER_SOURCE_DIR}/timer-slack.conf
+ DESTINATION /etc/resourced RENAME timer-slack.conf)
+ ENDIF()
+
+ IF("${VIP_AGENT}" STREQUAL "ON")
+ ADD_EXECUTABLE(vip-release-agent ${VIP_SOURCE_DIR}/vip-release-agent.c)
+ TARGET_LINK_LIBRARIES(vip-release-agent resourced_shared dlog)
+ INSTALL(TARGETS vip-release-agent DESTINATION bin)
+ INSTALL(FILES ${VIP_SOURCE_DIR}/vip-process.conf
+ DESTINATION
+ /etc/resourced RENAME vip-process.conf)
+ ENDIF()
+
+ IF("${BLOCK_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${BLOCK_SOURCE_DIR}/block.conf
+ DESTINATION /etc/resourced RENAME block.conf)
+ ENDIF()
+
+ IF("${FREEZER_MODULE}" STREQUAL "ON")
+ INSTALL(FILES ${FREEZER_SOURCE_DIR}/freezer.conf
+ DESTINATION /etc/resourced RENAME freezer.conf)
++ INSTALL(FILES ${FREEZER_SOURCE_DIR}/libsystem-freezer.so.${ARCH} DESTINATION ${LIB_INSTALL_DIR}
+ RENAME libsystem-freezer.so)
+ ENDIF()
+
+ INSTALL(FILES ${PROC-STAT_SOURCE_DIR}/proc.conf
+ DESTINATION /etc/resourced RENAME proc.conf)
+
+ ADD_EXECUTABLE(hashtest ${UTILS_SOURCE_DIR}/hashtest.c)
+ TARGET_LINK_LIBRARIES(hashtest ${RESOURCED_REQUIRE_PKGS_LDFLAGS})
+
+ ADD_SUBDIRECTORY(mem-stress)
+ ADD_SUBDIRECTORY(${PROC-STAT_SOURCE_DIR})
+ ADD_SUBDIRECTORY(${MEMPS_SOURCE_DIR})
+ ADD_SUBDIRECTORY(${NETWORK_SOURCE_DIR})
+ IF("${SLP_TESTS}" STREQUAL "ON")
+ ADD_SUBDIRECTORY(${TEST_DIR})
+ ENDIF()
--- /dev/null
- return (gboolean)strstr((char *)user_data, (char *)key);
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @file block-monitor.c
+ *
+ * @desc monitor file system using fanotify
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+ #include <dirent.h>
+ #include <stdbool.h>
+ #include <errno.h>
+ #include <ctype.h>
+ #include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/time.h>
+ #include <sys/stat.h>
+ #include <sys/resource.h>
+ #include <sys/syscall.h>
+ #include <sys/fanotify.h>
+ #include <sys/mount.h>
+ #include <mntent.h>
+ #include <Ecore.h>
+
+ #include "notifier.h"
+ #include "procfs.h"
+ #include "proc-common.h"
+ #include "macro.h"
+ #include "module.h"
+ #include "module-data.h"
+ #include "resourced.h"
+ #include "trace.h"
+ #include "config-parser.h"
+ #include "const.h"
+ #include "file-helper.h"
+ #include "block.h"
+ #include "logging.h"
+
+ #define FAN_MODE_ACCESS "ACCESS"
+ #define FAN_MODE_READ "READ"
+ #define FAN_MODE_WRITE "WRITE"
+ #define FAN_MODE_DISALBE "DISALBE"
+ #define MAX_LOGGING_LIMIT 0x20000
+
+ enum block_mount_type {
+ BLOCK_MOUNT_ORIGINAL,
+ BLOCK_MOUNT_BIND,
+ };
+
+ enum block_logging_type {
+ BLOCK_LOGGING_NONE = 0x0,
+ BLOCK_LOGGING_DLOG = 0x1,
+ BLOCK_LOGGING_FILE = 0x2,
+ BLOCK_LOGGING_DB = 0x4,
+ };
+
+ static gboolean find_hash(gpointer key, gpointer value, gpointer user_data)
+ {
++ if (!user_data || !key)
++ return FALSE;
++
++ return (strstr((char *)user_data, (char *)key) ? TRUE : FALSE);
+ }
+
+ int convert_fanotify_mode(const char *mode)
+ {
+ int famode = FAN_EVENT_ON_CHILD;
+ if (!strncmp(mode, FAN_MODE_ACCESS, sizeof(FAN_MODE_ACCESS)))
+ famode |= FAN_OPEN;
+ else if (!strncmp(mode, FAN_MODE_READ, sizeof(FAN_MODE_READ)))
+ famode |= FAN_CLOSE_NOWRITE;
+ else if (!strncmp(mode, FAN_MODE_WRITE, sizeof(FAN_MODE_WRITE)))
+ famode |= FAN_CLOSE_WRITE;
+ else if (!strncmp(mode, FAN_MODE_DISALBE, sizeof(FAN_MODE_DISALBE)))
+ famode = 0;
+ return famode;
+ }
+
+ static bool check_mount_dest(const char *path)
+ {
+ bool ret = false;
+ struct mntent *mnt;
+ const char *table = "/etc/mtab";
+ FILE *fp;
+ int len = strlen(path);
+
+ fp = setmntent(table, "r");
+ if (!fp)
+ return ret;
+
+ do {
+ mnt = getmntent(fp);
+ if (mnt && !strncmp(mnt->mnt_dir, path, len)) {
+ ret = true;
+ break;
+ }
+ } while (mnt != NULL);
+ endmntent(fp);
+ return ret;
+ }
+
+ static void block_logging(struct block_monitor_info *bmi, pid_t pid,
+ char *label, char *filename)
+ {
+ int type = bmi->logging;
+
+ if (type & BLOCK_LOGGING_DLOG)
+ _E("pid %d(%s) accessed %s", pid, label, filename);
+
+ if (type & BLOCK_LOGGING_FILE) {
+ FILE *f;
+
+ if (bmi->logpath == NULL) {
+ bmi->logpath = malloc(MAX_PATH_LENGTH);
+ if (!bmi->logpath) {
+ _E("not enough memory");
+ return;
+ }
+ snprintf(bmi->logpath, MAX_PATH_LENGTH-1, "/var/log/%s.log", strrchr(bmi->path, '/'));
+ }
+
+ f = fopen(bmi->logpath, "a+");
+ if (!f)
+ return;
+ bmi->total_loglen += fprintf(f, "pid %d(%s) accessed %s\n", pid, label, filename);
+ fclose(f);
+ if (bmi->total_loglen > MAX_LOGGING_LIMIT) {
+ if (unlink(bmi->logpath) < 0)
+ _E("fail to remove %s file\n", bmi->logpath);
+ else
+ _I("clear previous log files");
+ }
+ }
+
+ if (type & BLOCK_LOGGING_DB) {
+ struct logging_data ld;
+
+ /*
+ * label in the xattr meant owner package name.
+ * block logging needed only package name.
+ * For removing overhead, block reqeusted to write label instead of appid or pkgid.
+ */
+ ld.appid = ld.pkgid = label;
+ ld.data = filename;
+ resourced_notify(RESOURCED_NOTIFIER_LOGGING_WRITE, &ld);
+ }
+ }
+
+ static Eina_Bool block_monitor_cb(void *user_data, Ecore_Fd_Handler *fd_handler)
+ {
+ int fd, n, ret;
+ struct fanotify_event_metadata *m;
+ union {
+ struct fanotify_event_metadata metadata;
+ char buffer[4096];
+ } data;
+ pid_t currentpid = getpid();
+ struct block_monitor_info *bmi = (struct block_monitor_info *)user_data;
+ gpointer hash = 0;
+
+ if (!ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_READ)) {
+ _E("ecore_main_fd_handler_active_get error , return\n");
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ fd = ecore_main_fd_handler_fd_get(fd_handler);
+ if (fd < 0) {
+ _E("ecore_main_fd_handler_fd_get error, return\n");
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ n = read(fd, &data, sizeof(data));
+ if (n < 0) {
+ _E("Failed to read fanotify event\n");
+ if (errno == EINTR || errno == EAGAIN || errno == EACCES)
+ return ECORE_CALLBACK_RENEW;
+ else
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
+ char fn[sizeof("/proc/self/fd/") + MAX_DEC_SIZE(int)];
+ char buf[MAX_PATH_LENGTH] = {0,};
+ char label[PROC_NAME_MAX];
+
+ if (m->fd < 0)
+ goto next;
+ if (m->pid == currentpid)
+ goto next;
+ snprintf(fn, sizeof(fn), "/proc/self/fd/%d", m->fd);
+ ret = readlink(fn, buf, sizeof(buf)-1);
+ if (ret < 0)
+ goto next;
+
+ if (bmi->last_skip_pid == m->pid)
+ goto next;
+
+ if (bmi->last_monitor_pid == m->pid)
+ goto logging;
+
+ hash = bmi->block_exclude_path
+ ? g_hash_table_find(bmi->block_exclude_path,
+ find_hash, (gpointer)buf)
+ : NULL;
+
+ if (hash) {
+ bmi->last_skip_pid = m->pid;
+ goto next;
+ }
+
+ ret = proc_get_label(m->pid, label);
+ if (ret < 0)
+ goto next;
+
+ if (bmi->block_include_proc) {
+ hash = g_hash_table_find(bmi->block_include_proc,
+ find_hash, (gpointer)label);
+ if (!hash) {
+ bmi->last_skip_pid = m->pid;
+ goto next;
+ }
+ }
+
+ logging:
+ bmi->last_monitor_pid = m->pid;
+ block_logging(bmi, m->pid, label, buf);
+
+ next:
+ close(m->fd);
+ }
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ static void block_logging_init(struct block_monitor_info *bmi)
+ {
+ if (bmi->logging & BLOCK_LOGGING_DB) {
+ static const struct module_ops *heart;
+ heart = find_module("HEART");
+ if (!heart)
+ bmi->logging &= (~BLOCK_LOGGING_DB);
+ }
+ }
+
+ int register_fanotify(struct block_monitor_info *bmi)
+ {
+ int ret;
+
+ if (!bmi || !strlen(bmi->path))
+ return RESOURCED_ERROR_NO_DATA;
+
+ _D("monitor register : path %s, mode %d", bmi->path, bmi->mode);
+
+ bmi->mfd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK | FAN_CLASS_CONTENT,
+ O_RDONLY | O_LARGEFILE | O_CLOEXEC | O_NOATIME);
+ if (bmi->mfd< 0) {
+ _E("Failed to create fanotify fd");
+ goto error;
+ }
+ if (!check_mount_dest(bmi->path)) {
+ ret = mount(bmi->path, bmi->path, 0, MS_BIND, 0);
+ if (ret) {
+ _E("Failed to mount monitor dir");
+ goto error;
+ }
+ bmi->mount = BLOCK_MOUNT_BIND;
+ }
+ if (fanotify_mark(bmi->mfd, FAN_MARK_ADD | FAN_MARK_MOUNT,
+ bmi->mode, AT_FDCWD, bmi->path) < 0) {
+ _E("Failed to mark fsnotify for %s", bmi->path);
+ goto error;
+ }
+ bmi->fd_handler = ecore_main_fd_handler_add(
+ bmi->mfd, ECORE_FD_READ, block_monitor_cb, bmi, NULL, NULL);
+ block_logging_init(bmi);
+ return RESOURCED_ERROR_NONE;
+ error:
+ if (bmi->mfd) {
+ close(bmi->mfd);
+ bmi->mfd = 0;
+ }
+ if (bmi->logpath) {
+ free(bmi->logpath);
+ bmi->logpath = NULL;
+ }
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ void unregister_fanotify(struct block_monitor_info *bmi)
+ {
+ int ret;
+
+ if (bmi) {
+ if (bmi->logpath)
+ free(bmi->logpath);
+ if (bmi->mount == BLOCK_MOUNT_BIND) {
+ ret = umount(bmi->path);
+ if (ret)
+ _E("Failed to umount partition : %s", bmi->path);
+ bmi->mount = BLOCK_MOUNT_ORIGINAL;
+ }
+ close(bmi->mfd);
+ ecore_main_fd_handler_del(bmi->fd_handler);
+ }
+ }
--- /dev/null
-
+ /*
+ * resourced
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @file freezer.c
+ *
+ * @desc Freezer module
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+ #include <dlfcn.h>
+
+ #include "macro.h"
+ #include "util.h"
+ #include "module.h"
+ #include "module-data.h"
+ #include "notifier.h"
+ #include "edbus-handler.h"
+ #include "resourced.h"
+ #include "trace.h"
+ #include "vconf.h"
+ #include "config-parser.h"
+ #include "procfs.h"
+ #include "proc-common.h"
-#define FREEZER_MODULE_PATH "/usr/lib/libsystem-freezer.so"
+ #include "freezer.h"
+
-static const struct module_ops freezer_modules_ops = {
++#define FREEZER_MODULE_PATH SYSTEM_LIB_PATH"/libsystem-freezer.so"
+
+ static int freezer_init_check;
+
+ /******************************************** Freezer symbols *************************************************/
+ /* Freezer cgroup late control setting retrieval */
+ int (*resourced_freezer_get_proc_late_control)(void) = NULL;
+
+ /* Freezer suspend status retrieval */
+ static bool (*resourced_freezer_is_suspended)(void) = NULL;
+
+ /* Freezer cgroup operation state */
+ static enum freezer_state (*resourced_freezer_get_operation_state)(void) = NULL;
+
+ /* Freezer module initialization and finalization functions */
+ static int (*resourced_freezer_initialize)(void *data) = NULL;
+ static int (*resourced_freezer_deinitialize)(void) = NULL;
+
+ /* Freezer module dbus method calls and signal handlers */
+ static DBusMessage *(*resourced_freezer_dbus_method_handler)(E_DBus_Object *obj, DBusMessage *msg) = NULL;
+ static void (*resourced_freezer_dbus_signal_handler)(void *data, DBusMessage *msg) = NULL;
+
+ /* Resourced notifier handlers */
+ static int (*resourced_freezer_change_state_cb)(void *data) = NULL;
+ static int (*resourced_freezer_service_launch)(void *data) = NULL;
+ static int (*resourced_freezer_wakeup)(void *data) = NULL;
+ static int (*resourced_freezer_service_wakeup)(void *data) = NULL;
+ static int (*resourced_freezer_app_suspend)(void *data) = NULL;
+ static int (*resourced_freezer_power_off)(void *data) = NULL;
+
+ /* dlopen handle for libfreezer */
+ static void *dlopen_handle;
+ /****************************************** Freezer symbols end ***********************************************/
+
+ /* freezer_proc_get_late_control function defined for freezer module on case */
+ int resourced_freezer_proc_late_control(void)
+ {
+ if (resourced_freezer_get_proc_late_control)
+ return resourced_freezer_get_proc_late_control();
+
+ _E("freezer_get_proc_late_control is not loaded!");
+ return 0;
+ }
+
+ /****************************************** Internal symbols **************************************************/
+ static DBusMessage *resourced_freezer_dbus_method_handler_generic(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ int ret;
+
+ if (resourced_freezer_dbus_method_handler)
+ return resourced_freezer_dbus_method_handler(obj, msg);
+
+ ret = 0;
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
+ return reply;
+ }
+
+ static struct edbus_method edbus_methods[] = {
+ { METHOD_GET_FREEZER_STATE, NULL, "i", resourced_freezer_dbus_method_handler_generic },
+ { METHOD_GET_FREEZER_SERVICE, NULL, "i", resourced_freezer_dbus_method_handler_generic },
+ /* Add methods here */
+ };
+
+ static struct edbus_method edbus_suspend_methods[] = {
+ { METHOD_SET_FREEZER_SUSPEND, "s", "i", resourced_freezer_dbus_method_handler_generic },
+ /* Add methods here */
+ };
+
+ static void freezer_dbus_init(bool is_suspend)
+ {
+ resourced_ret_c ret;
+
+ register_edbus_signal_handler(RESOURCED_PATH_FREEZER,
+ RESOURCED_INTERFACE_FREEZER,
+ SIGNAL_FREEZER_STATUS,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+ register_edbus_signal_handler(RESOURCED_PATH_FREEZER,
+ RESOURCED_INTERFACE_FREEZER,
+ SIGNAL_FREEZER_SERVICE,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_FREEZER,
+ edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ ret_msg_if(ret != RESOURCED_ERROR_NONE,
+ "DBus method registration for %s is failed",
+ RESOURCED_PATH_FREEZER);
+
+ if (!is_suspend)
+ return;
+
+ register_edbus_signal_handler(DEVICED_PATH_DISPLAY,
+ DEVICED_INTERFACE_DISPLAY,
+ SIGNAL_DEVICED_LCDONCOMPLETE,
+ (void *)resourced_freezer_dbus_signal_handler,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_FREEZER,
+ edbus_suspend_methods,
+ ARRAY_SIZE(edbus_suspend_methods));
+ ret_msg_if(ret != RESOURCED_ERROR_NONE,
+ "DBus method registration for %s is failed",
+ RESOURCED_PATH_FREEZER);
+ }
+
+ static bool freezer_is_present(void)
+ {
+ struct stat buf;
+
+ /* Check if libfreezer.so is present or not */
+ if (stat(FREEZER_MODULE_PATH, &buf)) {
+ _E("Freezer library is not present @ %s", FREEZER_MODULE_PATH);
+ return false;
+ }
+
+ return true;
+ }
+
+ static void freezer_unload_symbols(void)
+ {
+ #define FREEZER_UNLOAD_SYMBOL(sym) \
+ sym = NULL;
+
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_get_proc_late_control);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_is_suspended);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_get_operation_state);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_initialize);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_deinitialize);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_dbus_method_handler);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_dbus_signal_handler);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_change_state_cb);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_service_launch);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_wakeup);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_service_wakeup);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_app_suspend);
+ FREEZER_UNLOAD_SYMBOL(resourced_freezer_power_off);
+
+ #undef FREEZER_UNLOAD_SYMBOL
+
+ if (dlopen_handle) {
+ dlclose(dlopen_handle);
+ dlopen_handle = NULL;
+ }
+ }
+
+ static int freezer_load_symbols(void)
+ {
+ dlopen_handle = dlopen(FREEZER_MODULE_PATH, RTLD_NOW);
+ if (!dlopen_handle) {
+ _E("freezer dlopen failed!");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ #define FREEZER_LOAD_SYMBOL(sym, name) \
+ sym = dlsym(dlopen_handle, name); \
+ if (!sym) { \
+ _E("failed to dlsym %s", name); \
+ goto error; \
+ } \
+
+ FREEZER_LOAD_SYMBOL(resourced_freezer_get_proc_late_control, "freezer_get_proc_late_control");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_is_suspended, "freezer_is_suspended");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_get_operation_state, "freezer_get_operation_state");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_initialize, "freezer_initialize");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_deinitialize, "freezer_finalize");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_dbus_method_handler, "freezer_dbus_method_handler");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_dbus_signal_handler, "freezer_dbus_signal_handler");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_change_state_cb, "freezer_change_state_cb");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_service_launch, "freezer_service_launch");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_wakeup, "freezer_wakeup");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_service_wakeup, "freezer_service_wakeup");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_app_suspend, "freezer_app_suspend");
+ FREEZER_LOAD_SYMBOL(resourced_freezer_power_off, "freezer_power_off");
+
+ #undef FREEZER_LOAD_SYMBOL
+
+ return RESOURCED_ERROR_NONE;
+ error:
+ freezer_unload_symbols();
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ static int resourced_freezer_init(void *data)
+ {
+ int ret_code;
+ bool is_suspend;
+ bool is_present;
+ struct freezer_init_data init_data = { .resourced_app_list = &proc_app_list };
+
+ is_present = freezer_is_present();
+ if (!is_present) {
+ _E("Freezer library not present. Not enabling freezer");
+ return RESOURCED_ERROR_FAIL;
+ }
+ ret_code = freezer_load_symbols();
+ if (ret_code != RESOURCED_ERROR_NONE) {
+ _E("Not able to load symbols. Will use default definitions");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret_code = resourced_freezer_initialize(&init_data);
+ ret_value_msg_if(ret_code < 0, ret_code, "failed to initialize freezer module\n");
+
+ is_suspend = resourced_freezer_is_suspended();
+ freezer_dbus_init(is_suspend);
+
+ if (is_suspend)
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+ resourced_freezer_service_launch);
+ register_notifier(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ resourced_freezer_change_state_cb);
+ register_notifier(RESOURCED_NOTIFIER_APP_WAKEUP,
+ resourced_freezer_wakeup);
+ register_notifier(RESOURCED_NOTIFIER_SERVICE_WAKEUP,
+ resourced_freezer_service_wakeup);
+ register_notifier(RESOURCED_NOTIFIER_APP_SUSPEND,
+ resourced_freezer_app_suspend);
+ register_notifier(RESOURCED_NOTIFIER_POWER_OFF,
+ resourced_freezer_power_off);
+
+ freezer_init_check = 1;
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int resourced_freezer_finalize(void *data)
+ {
+ if (freezer_init_check == 0)
+ return RESOURCED_ERROR_NONE;
+
+ freezer_init_check = 0;
+
+ resourced_freezer_deinitialize();
+
+ unregister_notifier(RESOURCED_NOTIFIER_FREEZER_CGROUP_STATE,
+ resourced_freezer_change_state_cb);
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_LAUNCH,
+ resourced_freezer_service_launch);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_WAKEUP,
+ resourced_freezer_wakeup);
+ unregister_notifier(RESOURCED_NOTIFIER_SERVICE_WAKEUP,
+ resourced_freezer_service_wakeup);
+ unregister_notifier(RESOURCED_NOTIFIER_APP_SUSPEND,
+ resourced_freezer_app_suspend);
+ unregister_notifier(RESOURCED_NOTIFIER_POWER_OFF,
+ resourced_freezer_power_off);
+
+ freezer_unload_symbols();
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int resourced_freezer_status(void *data)
+ {
+ struct freezer_status_data *f_data;
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (!freezer_init_check)
+ return RESOURCED_ERROR_NONE;
+
+ f_data = (struct freezer_status_data *)data;
+ switch (f_data->type) {
+ case GET_STATUS:
+ ret = resourced_freezer_get_operation_state();
+ break;
+ default:
+ _E("Unsupported command: %d; status", f_data->type);
+ }
+
+ return ret;
+ }
+
++static struct module_ops freezer_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "freezer",
+ .init = resourced_freezer_init,
+ .exit = resourced_freezer_finalize,
+ .status = resourced_freezer_status,
+ };
+
+ MODULE_REGISTER(&freezer_modules_ops)
--- /dev/null
- decisions[DECISION_THREAD_UPDATE].thread = (int)NULL;
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * @file decision.c
+ *
+ * @desc decision for resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+ #include <Ecore.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <time.h>
+ #include <glib.h>
+ #include <sys/resource.h>
+
+ #include "resourced.h"
+ #include "trace.h"
+ #include "module.h"
+ #include "macro.h"
+ #include "notifier.h"
+ #include "proc-common.h"
+ #include "heart.h"
+ #include "logging.h"
+ #include "heart-common.h"
+ #include "edbus-handler.h"
+ #include "decision.h"
+ #include "filemap.h"
+
+ #define DECISION_FILE_PATH HEART_FILE_PATH"/.decision.dat"
+ #define DECISION_FILE_SIZE (512 * 1024)
+ /* To Do: make configurable */
+ #define DECISION_WRITE_INTERVAL 60 * 30 /* 30 minute */
+ #define DECISION_PRIORITY 20
+
+ enum {
+ DECISION_THREAD_UPDATE,
+ DECISION_THREAD_WRITE,
+ DECISION_THREAD_MAX,
+ };
+
+ static pthread_mutex_t decision_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static GHashTable *decision_app_list;
+
+ static pthread_mutex_t decision_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static GQueue *decision_queue;
+ static struct filemap *fm;
+
+ static const struct decision_module *decision_modules[DECISION_MAX];
+ static int num_decisions;
+
+ struct decision_thread {
+ pthread_t thread;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ };
+ static struct decision_thread decisions[DECISION_THREAD_MAX];
+
+ static Ecore_Timer *decision_write_timer = NULL;
+
+ static const struct decision_module *decision_module_find(int type)
+ {
+ if (type < 0 || type >= DECISION_MAX)
+ return NULL;
+
+ return decision_modules[type];
+ }
+
+ static void* decision_info_find(struct decision_table *dt, int type)
+ {
+ if (!dt || type < 0 || type >= DECISION_MAX)
+ return NULL;
+
+ return dt->infos[type];
+ }
+
+ static int decision_table_init(struct decision_table *dt, const char *appid,
+ const char *pkgname)
+ {
+ int i;
+
+ if (!dt) {
+ _E("parameter dt is null");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dt->ai = resourced_appinfo_get(dt->ai, appid, pkgname);
+
+ assert(dt->ai);
+
+ dt->updated = 0;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm;
+ void *info;
+
+ dm = decision_module_find(i);
+ if (!dm) {
+ _E("cannot find decision module");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ info = dm->create();
+
+ if (!info) {
+ _E("cannot create info for type %d", i);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dt->infos[i] = info;
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static struct decision_table *decision_table_new(const char *appid,
+ const char *pkgname)
+ {
+ struct decision_table *dt;
+ int ret;
+
+ dt = calloc(1, sizeof(struct decision_table));
+ if (!dt) {
+ _E("fail to allocate decision_table");
+ return NULL;
+ }
+
+ ret = decision_table_init(dt, appid, pkgname);
+ if (ret < 0) {
+ _E("fail to init decision table for appid = %s", appid);
+ free(dt);
+ return NULL;
+ }
+
+ g_hash_table_insert(decision_app_list, (gpointer)dt->ai->appid,
+ (gpointer)dt);
+
+ return dt;
+ }
+
+ static void decision_table_free(gpointer value)
+ {
+ struct decision_table *dt = (struct decision_table *)value;
+ int i;
+
+ if (!dt)
+ return;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm;
+ void * info;
+
+ dm = decision_module_find(i);
+ info = decision_info_find(dt, i);
+
+ if (dm && info)
+ dm->free(info);
+ }
+ resourced_appinfo_put(dt->ai);
+ free(dt);
+ }
+
+ static struct decision_table *decision_table_find(const char *appid,
+ const char *pkgname)
+ {
+ struct decision_table *dt;
+
+ if (!decision_app_list) {
+ _E("decision app list was not created");
+ return NULL;
+ }
+
+ dt = g_hash_table_lookup(decision_app_list, (gconstpointer)appid);
+ if (!dt) {
+ dt = decision_table_new(appid, pkgname);
+ if (!dt) {
+ _E("cannot create decision table for %s", appid);
+ return NULL;
+ }
+ }
+
+ return dt;
+ }
+
+ struct decision_item *decision_item_new(int type, const char *appid, const char *pkgname)
+ {
+ struct decision_item *di;
+
+ di = malloc(sizeof(struct decision_item));
+ if (!di) {
+ _E("fail to allocate decision_item");
+ return NULL;
+ }
+
+ memset(di, 0, sizeof(struct decision_item));
+ di->type = type;
+ di->ai = resourced_appinfo_get(di->ai, appid, pkgname);
+
+ if (!di->ai) {
+ _E("fail to get appinfo for appid = %s, pkgname = %s",
+ appid, pkgname);
+ free(di);
+ return NULL;
+ }
+
+ return di;
+ }
+
+ static void decision_item_free(struct decision_item *di)
+ {
+ if (!di)
+ return;
+
+ resourced_appinfo_put(di->ai);
+ free(di);
+ }
+
+
+ static int decision_item_update(struct decision_item *di)
+ {
+ const struct decision_module *dm;
+ struct decision_table *dt;
+ void *info;
+
+ if (!di || !di->ai) {
+ _E("invalid parameter decision item or null appinfo");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pthread_mutex_lock(&decision_list_mutex);
+ dt = decision_table_find(di->ai->appid, di->ai->pkgname);
+ if (!dt) {
+ _E("there is no decision table for %s", di->ai->appid);
+ pthread_mutex_unlock(&decision_list_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+ pthread_mutex_unlock(&decision_list_mutex);
+
+ info = decision_info_find(dt, di->type);
+ if (!info) {
+ _E("there is no info for type = %d", di->type);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* call sub module's update function */
+ dm = decision_module_find(di->type);
+ if (!dm) {
+ _E("invalid module index = %d", di->type);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ dm->update(di, info);
+
+ dt->updated = 1;
+ _D("update item for module %d for appid %s", dm->type, dt->ai->appid);
+
+ decision_item_free(di);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int decision_queue_item_insert(struct decision_item *di)
+ {
+ if (!di)
+ return RESOURCED_ERROR_FAIL;
+
+ pthread_mutex_lock(&decision_queue_mutex);
+ g_queue_push_tail(decision_queue, (gpointer)di);
+ pthread_mutex_unlock(&decision_queue_mutex);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int decision_update(void)
+ {
+ struct decision_item *di;
+ int ret;
+
+ pthread_mutex_lock(&decision_queue_mutex);
+ while (!g_queue_is_empty(decision_queue)) {
+ di = g_queue_pop_head(decision_queue);
+
+ ret = decision_item_update(di);
+
+ if (ret < 0) {
+ _E("fail to update item");
+ pthread_mutex_unlock(&decision_queue_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ pthread_mutex_unlock(&decision_queue_mutex);
+ _D("finish update deicion item");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static void decision_entry_read(const struct filemap_info *fi)
+ {
+ if (!fi)
+ return;
+
+ _D("read: name = %s, value = %s", fi->key, fi->value);
+ }
+
+ static int decision_table_write(struct decision_table *dt)
+ {
+ int i, size = 0;
+ /* buffer includes '\n' and null */
+ char buf[DECISION_BUF_MAX] = {0, };
+ char result_buf[DECISION_BUF_MAX] = {0, };
+ int ret = RESOURCED_ERROR_NONE;
+
+ if (!dt) {
+ _E("decision table is null or appinfo is null");
+ return RESOURCED_ERROR_FAIL;
+ }
+ assert(dt->ai);
+
+ if (!dt->updated)
+ return ret;
+
+ for (i = 0; i < DECISION_MAX; i++) {
+ const struct decision_module *dm = decision_module_find(i);
+ void *info = decision_info_find(dt, i);
+
+ if (!dm || !info) {
+ _E("invalid decision module or decision info");
+ continue;
+ }
+
+ dm->write(info, result_buf, DECISION_BUF_MAX);
+
+ size += snprintf(buf + size, DECISION_BUF_MAX - size, "\t%s",
+ result_buf);
+
+ if (size >= DECISION_BUF_MAX) {
+ _E("write buffer size exceeded size = %d", size);
+ break;
+ }
+ }
+
+ ret = filemap_write(fm, dt->ai->appid, buf, &(dt->offset));
+ if (ret < 0)
+ _E("cannot write buf %s for %s", buf, dt->ai->appid);
+
+ _I("decision write buf: %s", buf);
+ dt->updated = 0;
+
+ return ret;
+ }
+
+ static int decision_write(void)
+ {
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ struct decision_table *dt;
+ int ret;
+
+ g_hash_table_iter_init(&iter, decision_app_list);
+
+ pthread_mutex_lock(&decision_list_mutex);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ dt = (struct decision_table *)value;
+
+ ret = decision_table_write(dt);
+ if (ret < 0)
+ _E("decision table write failed for %s", key);
+ }
+
+ pthread_mutex_unlock(&decision_list_mutex);
+
+ filemap_foreach_read(fm, filemap_root_node(fm), decision_entry_read);
+
+ return ret;
+ }
+
+ static int decision_app_terminated_cb(void *data)
+ {
+ struct proc_status *ps = (struct proc_status *)data;
+ struct decision_table *dt;
+ int ret;
+
+ if (!ps || !ps->pai || !ps->pai->program)
+ return RESOURCED_ERROR_FAIL;
+
+ pthread_mutex_lock(&decision_list_mutex);
+
+ dt = decision_table_find(ps->appid, ps->pai->program->pkgname);
+
+ if (!dt) {
+ pthread_mutex_unlock(&decision_list_mutex);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = decision_table_write(dt);
+ if (ret < 0)
+ _D("decision table write failed for %s", ps->appid);
+
+ g_hash_table_remove(decision_app_list, (gconstpointer)ps->appid);
+
+ pthread_mutex_unlock(&decision_list_mutex);
+ _I("%s table is removed from decision table", ps->appid);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static void *decision_update_thread(void *arg)
+ {
+ int ret;
+
+ setpriority(PRIO_PROCESS, 0, DECISION_PRIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of writing decision result.
+ */
+ ret = pthread_mutex_lock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&decisions[DECISION_THREAD_UPDATE].cond,
+ &decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision update thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret)
+ _E("decision update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ decision_update();
+
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("decision update thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* now our thread finishes - cleanup tid */
- decisions[DECISION_THREAD_WRITE].thread = (int)NULL;
++ decisions[DECISION_THREAD_UPDATE].thread = (long)NULL;
+
+ return NULL;
+ }
+
+ static void *decision_write_thread_main(void *arg)
+ {
+ int ret;
+
+ setpriority(PRIO_PROCESS, 0, DECISION_PRIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of writing decision result.
+ */
+ ret = pthread_mutex_lock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&decisions[DECISION_THREAD_WRITE].cond,
+ &decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret)
+ _E("decision write thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ decision_write();
+
+ ret = pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("decision write thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* now our thread finishes - cleanup tid */
- decisions[DECISION_THREAD_UPDATE].thread = (int)NULL;
++ decisions[DECISION_THREAD_WRITE].thread = (long)NULL;
+
+ return NULL;
+ }
+
+ static int decision_thread_create(void)
+ {
+ int ret = RESOURCED_ERROR_NONE;
+
+ pthread_mutex_init(&decisions[DECISION_THREAD_UPDATE].mutex, NULL);
+ pthread_cond_init(&decisions[DECISION_THREAD_UPDATE].cond, NULL);
+
+ /* initialize decision_update_thread */
+ if (!decisions[DECISION_THREAD_UPDATE].thread) {
+ ret = pthread_create(&decisions[DECISION_THREAD_UPDATE].thread, NULL,
+ (void *)decision_update_thread, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for decision_update_thread_main failed, %d\n", ret);
- decisions[DECISION_THREAD_WRITE].thread = (int)NULL;
++ decisions[DECISION_THREAD_UPDATE].thread = (long)NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+ _D("pthread creation for decision update success");
+ pthread_detach(decisions[DECISION_THREAD_UPDATE].thread);
+ }
+ _I("decision update thread %u is running",
+ decisions[DECISION_THREAD_UPDATE].thread);
+
+
+ pthread_mutex_init(&decisions[DECISION_THREAD_WRITE].mutex, NULL);
+ pthread_cond_init(&decisions[DECISION_THREAD_WRITE].cond, NULL);
+
+ /* initialize decision_write_thread */
+ if (!decisions[DECISION_THREAD_WRITE].thread) {
+ ret = pthread_create(&decisions[DECISION_THREAD_WRITE].thread, NULL,
+ (void *)decision_write_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for decision_write_thread failed, %d\n", ret);
++ decisions[DECISION_THREAD_WRITE].thread = (long)NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+ _D("pthread creation for decision write success");
+ pthread_detach(decisions[DECISION_THREAD_WRITE].thread);
+ }
+ _I("decision update thread %u is running",
+ decisions[DECISION_THREAD_WRITE].thread);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int decision_update_start(void)
+ {
+ int ret;
+
+ _D("decision update callback function start");
+
+ /* signal to decision update thread */
+ ret = pthread_mutex_trylock(&decisions[DECISION_THREAD_UPDATE].mutex);
+ if (ret) {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ pthread_cond_signal(&decisions[DECISION_THREAD_UPDATE].cond);
+ _I("send signal to decision update thread");
+ pthread_mutex_unlock(&decisions[DECISION_THREAD_UPDATE].mutex);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static Eina_Bool decision_send_signal_to_write(void *data)
+ {
+ int ret;
+
+ _D("decision write callback function start");
+
+ /* signal to decision write thread */
+ ret = pthread_mutex_trylock(&decisions[DECISION_THREAD_WRITE].mutex);
+ if (ret) {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ pthread_cond_signal(&decisions[DECISION_THREAD_WRITE].cond);
+ _I("send signal to decision write thread");
+ pthread_mutex_unlock(&decisions[DECISION_THREAD_WRITE].mutex);
+
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ static int decision_init(void)
+ {
+ int ret = RESOURCED_ERROR_NONE;
+
+ decision_app_list = g_hash_table_new_full(
+ g_str_hash,
+ g_str_equal,
+ NULL,
+ decision_table_free);
+ if (!decision_app_list) {
+ _E("fail to allocate decision app list");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ decision_queue = g_queue_new();
+
+ if (!decision_queue) {
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ g_queue_init(decision_queue);
+
+ ret = filemap_new(&fm, DECISION_FILE_PATH, DECISION_FILE_SIZE, 1);
+
+ if (ret < 0) {
+ _E("fail filemap_init");
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ g_queue_free(decision_queue);
+ decision_queue = NULL;
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ register_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
+ decision_app_terminated_cb);
+
+ if (decision_write_timer == NULL) {
+ _E("decision write timer start");
+ decision_write_timer = ecore_timer_add(DECISION_WRITE_INTERVAL,
+ decision_send_signal_to_write, (void *)NULL);
+ }
+
+ ret = decision_thread_create();
+
+ return ret;
+ }
+
+
+ static int decision_exit(void)
+ {
+ if (decision_app_list) {
+ g_hash_table_destroy(decision_app_list);
+ decision_app_list = NULL;
+ }
+
+ if (decision_queue) {
+ g_queue_free(decision_queue);
+ decision_queue = NULL;
+ }
+
+ ecore_timer_del(decision_write_timer);
+ decision_write_timer = NULL;
+
+ unregister_notifier(RESOURCED_NOTIFIER_APP_TERMINATED,
+ decision_app_terminated_cb);
+
+ filemap_destroy(fm);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int decision_module_register(const struct decision_module *dm)
+ {
+ if (!dm) {
+ _E("parameter decision module is NULL");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (num_decisions == 0)
+ decision_init();
+
+ if (decision_module_find(dm->type)) {
+ _E("%d is already exist", dm->type);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ decision_modules[dm->type] = dm;
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int decision_module_unregister(const struct decision_module *dm)
+ {
+ if (!dm) {
+ _E("parameter decision module is NULL");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ decision_modules[dm->type] = NULL;
+
+ num_decisions--;
+ if (num_decisions == 0)
+ decision_exit();
+
+ return RESOURCED_ERROR_NONE;
+ }
--- /dev/null
-static const struct module_ops heart_modules_ops = {
+ /*
+ * resourced
+ *
+ * Copyright (c) 2012 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * @file heart.c
+ *
+ * @desc start heart for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <glib.h>
+
+ #include "notifier.h"
+ #include "trace.h"
+ #include "module.h"
+ #include "macro.h"
+ #include "heart.h"
+ #include "logging.h"
+ #include "resourced.h"
+ #include "config-parser.h"
+ #include "edbus-handler.h"
+
+ static GSList *heart_module; /* module list */
+
+ void heart_module_add(const struct heart_module_ops *ops)
+ {
+ heart_module = g_slist_append(heart_module, (gpointer)ops);
+ }
+
+ void heart_module_remove(const struct heart_module_ops *ops)
+ {
+ heart_module = g_slist_remove(heart_module, (gpointer)ops);
+ }
+
+ static const struct heart_module_ops *heart_module_find(const char *name)
+ {
+ GSList *iter;
+ struct heart_module_ops *module;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ if (!strcmp(module->name, name))
+ return module;
+ }
+ return NULL;
+ }
+
+ static void heart_module_init(void *data)
+ {
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Initialize [%s] module\n", module->name);
+ if (module->init)
+ ret = module->init(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to initialize [%s] module\n", module->name);
+ }
+ }
+
+ static void heart_module_exit(void *data)
+ {
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Deinitialize [%s] module\n", module->name);
+ if (module->exit)
+ ret = module->exit(data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to deinitialize [%s] module\n", module->name);
+ }
+ }
+
+ static int heart_load_config(struct parse_result *result, void *user_data)
+ {
+ const struct heart_module_ops *ops;
+ int *count = (int *)user_data;
+
+ if (!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, HEART_CONF_SECTION))
+ return RESOURCED_ERROR_FAIL;
+
+ ops = heart_module_find(result->name);
+ if (!ops)
+ return RESOURCED_ERROR_FAIL;
+
+ if (!strcmp(result->value, "ON"))
+ *count = *count + 1;
+ else
+ heart_module_remove(ops);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static DBusMessage *edbus_update_data_list(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ int ret;
+ DBusMessage *reply = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID);
+ reply = dbus_message_new_method_return(msg);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return reply;
+ }
+
+ resourced_notify(RESOURCED_NOTIFIER_DATA_UPDATE, NULL);
+
+ logging_save_to_storage(true);
+ /* update data list from db */
+ logging_update(true);
+
+ return reply;
+ }
+
+ static DBusMessage *edbus_flush_cache(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ int ret;
+ DBusMessage *reply = NULL;
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID);
+ reply = dbus_message_new_method_return(msg);
+ if (!ret) {
+ _E("Wrong message arguments!");
+ return reply;
+ }
+ /* flush module cache */
+ logging_save_to_storage(true);
+
+ return reply;
+ }
+
+ static const struct edbus_method edbus_methods[] = {
+ { "UpdateDataList", NULL, NULL, edbus_update_data_list },
+ { "Flush", NULL, NULL, edbus_flush_cache }
+ };
+
+ static int resourced_heart_init(void *data)
+ {
+ int ret, module_num = 0;
+
+ config_parse(HEART_CONF_FILE_PATH, heart_load_config, &module_num);
+
+ if (!module_num) {
+ _E("all heart modules have been disabled");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ ret = edbus_add_methods(RESOURCED_PATH_LOGGING, edbus_methods, ARRAY_SIZE(edbus_methods));
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_LOGGING);
+ }
+
+ heart_module_init(data);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int resourced_heart_dump(FILE *fp, int mode, void *data)
+ {
+ GSList *iter;
+ const struct heart_module_ops *module;
+ int ret = RESOURCED_ERROR_NONE;
+
+ logging_save_to_storage(true);
+
+ gslist_for_each_item(iter, heart_module) {
+ module = (struct heart_module_ops *)iter->data;
+ _D("Dump [%s] module\n", module->name);
+ if (module->dump)
+ ret = module->dump(fp, mode, data);
+ if (ret != RESOURCED_ERROR_NONE)
+ _E("Fail to dump [%s] module\n", module->name);
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int resourced_heart_exit(void *data)
+ {
+ heart_module_exit(data);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
++static struct module_ops heart_modules_ops = {
+ .priority = MODULE_PRIORITY_HIGH,
+ .name = "HEART",
+ .init = resourced_heart_init,
+ .dump = resourced_heart_dump,
+ .exit = resourced_heart_exit,
+ };
+
+ MODULE_REGISTER(&heart_modules_ops)
--- /dev/null
- unsigned int read_len;
+ /*
+ * resourced
+ *
+ * Copyright (c) 2012 - 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /*
+ * @file logging.c
+ *
+ * @desc start logging system for resourced
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ */
+
+ #include <leveldb/c.h>
+ #include <Ecore.h>
+ #include <sqlite3.h>
+ #include <unistd.h>
+ #include <pthread.h>
+ #include <glib.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <time.h>
+ #include <string.h>
+ #include <stdarg.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <sys/time.h>
+ #include <sys/resource.h>
+ #include <assert.h>
+
+ #include "trace.h"
+ #include "heart.h"
+ #include "logging.h"
+ #include "resourced.h"
+ #include "macro.h"
+ #include "module.h"
+ #include "config-parser.h"
+ #include "notifier.h"
+
+
+ #define LOGGING_BUF_MAX 1024
+ #define LOGGING_PTIORITY 20
+
+ #define DB_SIZE_THRESHOLD (50<<20)
+ #define LOGGING_FILE_PATH HEART_FILE_PATH
+ #define CREATE_QUERY "CREATE TABLE IF NOT EXISTS %s (appid TEXT, pkgid TEXT, time INT, data TEXT, idx INT, PRIMARY KEY(time, idx));"
+ #define DELETE_QUERY_WITH_TIME "DELETE from %s where time < %d;"
+ #define DELETE_QUERY_WITH_DATA "DELETE from %s where data = ?;"
+ #define INSERT_QUERY "INSERT INTO %s values (?, ?, ?, ?, ?);"
+ #define SELECT_QUERY "SELECT * FROM %s WHERE time > %d AND time < %d;"
+
+ #define SELECT_BEGIN_QUERY "SELECT * FROM %s "
+ #define SELECT_WHERE_QUERY "WHERE"
+ #define SELECT_AND_QUERY " AND"
+ #define SELECT_APPID_QUERY " appid = \'%s\'"
+ #define SELECT_PKGID_QUERY " pkgid = \'%s\'"
+ #define SELECT_START_QUERY " time > %d"
+ #define SELECT_END_QUERY " time < %d"
+ #define SELECT_FINISH_QUERY ";"
+
+ #define FINALIZE_AND_RETURN_IF(cond, func, format, arg...) do \
+ { if (CONDITION(cond)) { \
+ func(format, ##arg); \
+ pthread_mutex_unlock(&(module->cache_mutex)); \
+ sqlite3_finalize(insert_stmt); \
+ sqlite3_finalize(delete_stmt); \
+ return; \
+ } } while (0)
+
+ enum { read_until_null = -1 };
+
+ struct logging_module {
+ char *name;
+ char *db_path;
+ sqlite3 *db;
+ enum logging_period max_period;
+ pthread_mutex_t cache_mutex;
+ logging_info_cb func;
+ time_t latest_update_time;
+ int saved_interval;
+ int updated_interval;
+ enum logging_interval save_interval;
+ enum logging_interval update_interval;
+ GQueue *cache;
+ GSList *listener;
+ };
+
+ struct logging_search {
+ char *appid;
+ char *pkgid;
+ time_t start;
+ time_t end;
+ void *user_data;
+ logging_info_cb func;
+ };
+
+ struct logging_listerner {
+ void (*func)(char *data);
+ };
+
+ static const struct module_ops logging_modules_ops;
+
+ static pthread_t logging_data_thread = 0;
+ static pthread_mutex_t logging_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_cond_t logging_data_cond = PTHREAD_COND_INITIALIZER;
+ static Ecore_Timer *logging_data_timer = NULL;
+
+ static pthread_t logging_update_thread = 0;
+ static pthread_mutex_t logging_update_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_cond_t logging_update_cond = PTHREAD_COND_INITIALIZER;
+ static Ecore_Timer *logging_update_timer = NULL;
+
+ static GArray *logging_modules;
+ static sqlite3 *logging_db;
+ static leveldb_t *logging_leveldb;
+ static leveldb_options_t *options;
+ static leveldb_readoptions_t *roptions;
+ static leveldb_writeoptions_t *woptions;
+
+ static struct logging_object *logging_instance = NULL;
+
+ time_t logging_get_time(int clk_id)
+ {
+ struct timespec ts;
+
+ if (clock_gettime(clk_id, &ts) == -1) {
+ _E("clock_gettime failed");
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ }
+
+ return ts.tv_sec;
+ }
+
+ long logging_get_time_ms(void)
+ {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
+ }
+
+ static struct logging_module *logging_find_module(char *name)
+ {
+ int i;
+
+ if (!logging_modules)
+ return NULL;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ struct logging_module *module = g_array_index(logging_modules,
+ struct logging_module *, i);
+ if (!strcmp(name, module->name)) {
+ return module;
+ }
+ }
+
+ return NULL;
+ }
+
+ static int logging_db_busy(void * UNUSED user, int attempts)
+ {
+ struct timespec req, rem;
+
+ _E("DB locked by another process, attempts number %d", attempts);
+
+ req.tv_sec = 0;
+ req.tv_nsec = 500000000;
+ nanosleep(&req, &rem);
+ return 1;
+ }
+
+ int logging_module_init_with_db_path(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval, const char *db_path)
+ {
+ int ret;
+ sqlite3 *db = NULL;
+ const char *path = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char buf[LOGGING_BUF_MAX] = {0, };
+ struct logging_module *module;
+
+ if (!logging_instance) {
+ logging_instance = (struct logging_object *)malloc(sizeof(struct logging_object));
+ if (!logging_instance) {
+ _E("Failed to malloc logging_instance");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ logging_instance->ref = 0;
+ logging_init(NULL);
+ }
+
+ logging_instance->ref++;
+
+ /* check*/
+ if (logging_find_module(name)) {
+ _E("%s is already exist", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (db_path) {
+ /* DB create */
+ if (sqlite3_open(db_path, &db) != SQLITE_OK) {
+ _E("%s DB open failed (%s)", db_path, sqlite3_errmsg(db));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = sqlite3_exec(db, "PRAGMA locking_mode = NORMAL", 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(db));
+ _E("Skip set busy handler.");
+ } else {
+ /* Set how many times we'll repeat our attempts for sqlite_step */
+ if (sqlite3_busy_handler(db, logging_db_busy, NULL) != SQLITE_OK) {
+ _E("Couldn't set busy handler!");
+ }
+ }
+
+ path = db_path;
+ } else {
+ db = logging_db;
+ path = LOGGING_DB_FILE_NAME;
+ }
+
+ /* create table using module name and field_forms */
+ snprintf(buf, LOGGING_BUF_MAX, CREATE_QUERY, name);
+ ret = sqlite3_prepare_v2(db, buf, read_until_null, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("create %s table failed %s", name, sqlite3_errmsg(db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ _E("create %s table failed %s", name, sqlite3_errmsg(db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ sqlite3_finalize(stmt);
+
+ module = malloc(sizeof(struct logging_module));
+
+ if (!module) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* make logging_module_inform and set module_inform */
+ module->db = db;
+ module->func = func;
+ module->latest_update_time = time(NULL);
+ module->save_interval = save_interval;
+ module->saved_interval = save_interval;
+ module->update_interval = update_interval;
+ module->updated_interval = update_interval;
+ module->listener = NULL;
+
+ if (asprintf(&(module->name), "%s", name) < 0) {
+ _E("asprintf failed");
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (asprintf(&(module->db_path), "%s", path) < 0) {
+ _E("asprintf failed");
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ module->max_period = max_period;
+
+ if (pthread_mutex_init(&module->cache_mutex, NULL) < 0) {
+ _E("%s module mutex_init failed %d", name, errno);
+ free(module->name);
+ free(module);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ module->cache = g_queue_new();
+
+ if (!module->cache) {
+ _E("g_queue_new failed");
+ free(module->name);
+ free(module);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ g_queue_init(module->cache);
+
+ g_array_append_val(logging_modules, module);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_module_init(char *name, enum logging_period max_period,
+ enum logging_interval save_interval, logging_info_cb func, enum logging_interval update_interval)
+ {
+ return logging_module_init_with_db_path(name, max_period, save_interval, func, update_interval, NULL);
+ }
+
+ int logging_module_exit(void)
+ {
+ if (!logging_instance)
+ return RESOURCED_ERROR_NONE;
+
+ logging_instance->ref--;
+
+ if (logging_instance->ref == 0) {
+ free(logging_instance);
+ logging_instance = NULL;
+ logging_exit(NULL);
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_register_listener(char *name, logging_listener_cb listener_cb)
+ {
+ GSList *registerd;
+ struct logging_listerner *listener;
+ struct logging_module *module;
+
+ if (!listener_cb) {
+ _E("invalid listern func");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ registerd = g_slist_find(module->listener, listener_cb);
+
+ if (registerd) {
+ _E("already registerd listener %x", listener_cb);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ listener = malloc(sizeof(struct logging_listerner));
+ if (!listener) {
+ _E("Fail to malloc for notifier!");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ listener->func = listener_cb;
+
+ module->listener = g_slist_append(module->listener, listener);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_unregister_listener(char *name, logging_listener_cb listener_cb)
+ {
+ GSList *registerd;
+ struct logging_module *module;
+
+ if (!listener_cb) {
+ _E("invalid listern func");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ registerd = g_slist_find(module->listener, listener_cb);
+
+ if (!registerd) {
+ _E("It is not registered listerner %x", listener_cb);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ module->listener = g_slist_remove(module->listener, registerd->data);
+
+ free(registerd->data);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_operate(char *name, char *appid, char *pkgid, time_t time, char *data, int operation)
+ {
+ /* Save data to cache */
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ table = malloc(sizeof(struct logging_table_form));
+
+ if (!table) {
+ _E("malloc failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (operation == INSERT) {
+ if (snprintf(table->appid, MAX_APPID_LENGTH, "%s", appid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (snprintf(table->pkgid, MAX_PKGNAME_LENGTH, "%s", pkgid) < 0) {
+ _E("snprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ table->time = time;
+
+ if (asprintf(&(table->data), "%s", data) < 0) {
+ _E("asprintf failed");
+ free(table);
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ table->operation = operation;
+
+ pthread_mutex_lock(&(module->cache_mutex));
+ g_queue_push_tail(module->cache, (gpointer)table);
+ pthread_mutex_unlock(&(module->cache_mutex));
+
+ /* call listners */
+ if (module->listener) {
+ GSList *iter;
+ struct logging_listerner *listener;
+ char buf[LOGGING_BUF_MAX];
+
+ gslist_for_each_item(iter, module->listener) {
+ listener = (struct logging_listerner *)iter->data;
+ snprintf(buf, LOGGING_BUF_MAX, "%s %s %d %s", appid, pkgid, (int)time, data);
+
+ listener->func(buf);
+ }
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_write(char *name, char *appid, char *pkgid, time_t time, char *data)
+ {
+ return logging_operate(name, appid, pkgid, time, data, INSERT);
+ }
+
+ int logging_delete(char *name, char *data)
+ {
+ time_t time = 0;
+ return logging_operate(name, NULL, NULL, time, data, DELETE);
+ }
+
+ int logging_leveldb_put(char *key, unsigned int key_len, char *value, unsigned int value_len)
+ {
+ char *err = NULL;
+
+ if (!key || !key_len || !value || !value_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_put(logging_leveldb, woptions, key, key_len, value, value_len, &err);
+ if (err != NULL) {
+ _E("Failed to put to leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_leveldb_putv(char *key, unsigned int key_len, const char *fmt, ...)
+ {
+ char *err = NULL;
+ va_list ap;
+ char value[LOGGING_BUF_MAX];
+ unsigned int value_len;
+
+ if (!key || !key_len || !fmt)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(value, LOGGING_BUF_MAX, fmt, ap);
+ va_end(ap);
+
+ value_len = strlen(value);
+ if (!value_len) {
+ _E("Failed to get length of string");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_put(logging_leveldb, woptions, key, key_len, value, value_len, &err);
+ if (err != NULL) {
+ _E("Failed to put to leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_leveldb_read(char *key, unsigned int key_len, char *value, unsigned int value_len)
+ {
++ size_t read_len;
+ char *err = NULL;
+ char *result = NULL;
+
+ if (!key || !key_len || !value || !value_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ result = leveldb_get(logging_leveldb, roptions, key, key_len, &read_len, &err);
+ if (err != NULL) {
+ _E("Failed to get from leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ if (value_len < read_len)
+ snprintf(value, value_len, "%s", result);
+ else
+ snprintf(value, read_len + 1, "%s", result);
+
+ free(result);
+
+ _D("%s:%s", key, value);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_leveldb_delete(char *key, unsigned int key_len)
+ {
+ char *err = NULL;
+
+ if (!key || !key_len)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!logging_leveldb) {
+ _E("leveldb is not initialized");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ leveldb_delete(logging_leveldb, woptions, key, key_len, &err);
+ if (err != NULL) {
+ _E("Failed to delete from leveldb");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ free(err);
+ err = NULL;
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_get_latest_in_cache(char *name, char *appid, char **data)
+ {
+ int i, len;
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!appid) {
+ _E("appid parameter should be valid");
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ pthread_mutex_lock(&(module->cache_mutex));
+ len = g_queue_get_length(module->cache);
+ if (!len) {
+ _I("%s cache is empty", module->name);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ *data = NULL;
+
+ for (i = 0; i < len; i++) {
+ table = g_queue_peek_nth(module->cache, i);
+
+ if (table &&
+ !strcmp(appid, table->appid))
+ *data = table->data;
+ }
+ pthread_mutex_unlock(&(module->cache_mutex));
+
+ if (!*data) {
+ _E("NOT found in cache %s", appid);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static void logging_cache_search(struct logging_table_form *data, struct logging_search *search)
+ {
+ /* search in cache */
+ /* true condition, call function */
+ if (search->appid) {
+ if (strcmp(search->appid, data->appid))
+ return;
+
+ if (search->start && search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->pkgid) {
+ if (strcmp(search->pkgid, data->pkgid))
+ return;
+
+ if (search->start && search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->start) {
+ if (search->start < data->time)
+ return;
+
+ if (search->end && search->end > data->time)
+ return;
+ } else if (search->end) {
+ if (search->end > data->time)
+ return;
+ }
+
+ search->func(data, search->user_data);
+ }
+
+ int logging_read_foreach(char *name, char *appid, char *pkgid,
+ time_t start_time, time_t end_time, logging_info_cb callback, void *user_data)
+ {
+ /* Read from storage (cache & db) */
+ int result;
+ int len;
+ time_t cur_time;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_table_form table;
+ struct logging_module *module;
+ struct logging_search search;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ cur_time = time(NULL);
+ search.appid = NULL;
+ search.pkgid = NULL;
+ search.start = 0;
+ search.end = cur_time;
+ search.func = callback;
+ search.user_data = user_data;
+
+ len = snprintf(buf, LOGGING_BUF_MAX, SELECT_BEGIN_QUERY, name);
+
+ if (appid) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.appid = appid;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_APPID_QUERY, appid);
+
+ if (start_time) {
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ } else if (pkgid) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.pkgid = pkgid;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_PKGID_QUERY, pkgid);
+
+ if (start_time) {
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ } else if (start_time) {
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_WHERE_QUERY);
+
+ search.start = start_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_START_QUERY, (int)start_time);
+ }
+ if (end_time && cur_time > end_time)
+ search.end = end_time;
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_AND_QUERY);
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_END_QUERY, (int)search.end);
+
+ len += snprintf(buf+len, LOGGING_BUF_MAX - len, SELECT_FINISH_QUERY);
+
+ /* search from db */
+ if (sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL) != SQLITE_OK) {
+ _E("select failed");
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ do {
+ result = sqlite3_step(stmt);
+ switch (result) {
+ case SQLITE_ROW:
+ snprintf(table.appid, MAX_APPID_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 0));
+ snprintf(table.pkgid, MAX_PKGNAME_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 1));
+ table.time = sqlite3_column_int(stmt, 2);
+ if (module->latest_update_time < table.time)
+ module->latest_update_time = table.time;
+ table.data = (char *)sqlite3_column_text(stmt, 3);
+
+ callback(&table, user_data);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ _E("select %s table failed %s", name, sqlite3_errmsg(module->db));
+ /* FALLTHROUGH */
+ default:
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ } while (result == SQLITE_ROW);
+
+ sqlite3_finalize(stmt);
+
+ /* search from cache */
+ if (!g_queue_is_empty(module->cache)) {
+ pthread_mutex_lock(&(module->cache_mutex));
+ g_queue_foreach(module->cache, (GFunc)logging_cache_search, (gpointer)&search);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int logging_reset(char *name)
+ {
+ /* Table cut using max period */
+ time_t curr_time, del_time;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_module *module;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ module = logging_find_module(name);
+
+ if (!module) {
+ _E("There is no %s module", name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ time(&curr_time);
+
+ switch (module->max_period) {
+ case ONE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(1);
+ break;
+ case THREE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(3);
+ break;
+ case SIX_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(6);
+ break;
+ case TWELVE_HOUR:
+ del_time = curr_time - HOUR_TO_SEC(12);
+ break;
+ case ONE_DAY:
+ del_time = curr_time - HOUR_TO_SEC(24);
+ break;
+ case ONE_WEEK:
+ del_time = curr_time - DAY_TO_SEC(7);
+ break;
+ case ONE_MONTH:
+ del_time = curr_time - MONTH_TO_SEC(1);
+ break;
+ case FOUR_MONTH:
+ del_time = curr_time - MONTH_TO_SEC(4);
+ break;
+ default:
+ _E("%s invalid max period", module->name);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+
+ snprintf(buf, LOGGING_BUF_MAX, DELETE_QUERY_WITH_TIME, name, (int)del_time);
+
+ if (sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL) != SQLITE_OK) {
+ _E("delete %s table failed %s", name, sqlite3_errmsg(module->db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (sqlite3_step(stmt) != SQLITE_DONE) {
+ _E("delete %s table failed %s", name, sqlite3_errmsg(module->db));
+ sqlite3_finalize(stmt);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ sqlite3_finalize(stmt);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int logging_check_storage_size(const char *db_path)
+ {
+ int ret;
+ struct stat db_stat = {0};
+
+ if (!db_path)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ ret = stat(db_path, &db_stat);
+
+ if (ret) {
+ _E("Failed to get statistics for %s errno %d",
+ db_path, errno);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ if (db_stat.st_size >= DB_SIZE_THRESHOLD)
+ return RESOURCED_ERROR_FAIL;
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ void logging_update(int force)
+ {
+ int i, ret;
+ sqlite3_stmt *stmt = NULL;
+ struct logging_table_form table;
+ struct logging_module *module;
+ char buf[LOGGING_BUF_MAX] = {0, };
+
+ if (!logging_modules)
+ return;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules,
+ struct logging_module *, i);
+
+ /* no update when module update func is not registered */
+ if (module->func == NULL)
+ continue;
+
+ if (!force && module->updated_interval > 0) {
+ module->updated_interval -= ONE_MINUTE;
+ continue;
+ }
+
+ module->updated_interval = module->update_interval;
+
+ snprintf(buf, LOGGING_BUF_MAX, SELECT_QUERY, module->name,
+ (int)module->latest_update_time, (int)time(NULL));
+
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ _E("select failed");
+ sqlite3_finalize(stmt);
+ return;
+ }
+
+ do {
+ ret = sqlite3_step(stmt);
+ switch (ret) {
+ case SQLITE_ROW:
+ snprintf(table.appid, MAX_APPID_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 0));
+ snprintf(table.pkgid, MAX_PKGNAME_LENGTH, "%s", (char *)sqlite3_column_text(stmt, 1));
+ table.time = sqlite3_column_int(stmt, 2);
+ if (module->latest_update_time < table.time)
+ module->latest_update_time = table.time;
+ if (asprintf(&(table.data), "%s", (char *)sqlite3_column_text(stmt, 3)) < 0) {
+ _E("asprintf failed");
+ sqlite3_finalize(stmt);
+ return;
+ }
+ module->func(&table, NULL);
+ free(table.data);
+ break;
+ case SQLITE_DONE:
+ break;
+ case SQLITE_ERROR:
+ _E("select %s table failed %s", module->name,
+ sqlite3_errmsg(module->db));
+ /* FALLTHROUGH */
+ default:
+ sqlite3_finalize(stmt);
+ return;
+ }
+ } while (ret == SQLITE_ROW);
+
+ sqlite3_finalize(stmt);
+ }
+
+ }
+
+ void logging_save_to_storage(int force)
+ {
+ /* Save cache to storage */
+ static int index = 0;
+ int i, j, len, ret = 0;
+ sqlite3_stmt *insert_stmt = NULL;
+ sqlite3_stmt *delete_stmt = NULL;
+ char buf[LOGGING_BUF_MAX] = {0, };
+ struct logging_module *module;
+ struct logging_table_form *table;
+
+ if (!logging_modules)
+ return;
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules, struct logging_module *, i);
+
+ if (!force && module->saved_interval > 0) {
+ module->saved_interval -= ONE_MINUTE;
+ continue;
+ }
+
+ module->saved_interval = module->save_interval;
+
+ /* find q and pop */
+ pthread_mutex_lock(&(module->cache_mutex));
+ len = g_queue_get_length(module->cache);
+ if (!len) {
+ _I("%s cache is empty", module->name);
+ pthread_mutex_unlock(&(module->cache_mutex));
+ continue;
+ }
+
+ sqlite3_exec(module->db, "BEGIN;", NULL, NULL, NULL);
+
+ snprintf(buf, LOGGING_BUF_MAX, INSERT_QUERY, module->name);
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &insert_stmt, NULL);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ snprintf(buf, LOGGING_BUF_MAX, DELETE_QUERY_WITH_DATA, module->name);
+ ret = sqlite3_prepare_v2(module->db, buf, read_until_null, &delete_stmt, NULL);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ for (j = 0; j < len; j++) {
+ table = g_queue_peek_head(module->cache);
+ if (!table)
+ continue;
+
+ if (table->operation == DELETE) {
+ sqlite3_reset(delete_stmt);
+
+ ret = sqlite3_bind_text(delete_stmt, 1, table->data, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind data : %s for preparing statement", table->pkgid);
+
+ ret = sqlite3_step(delete_stmt);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_DONE, _E, "delete %s table failed %s", module->name, sqlite3_errmsg(module->db));
+
+ } else {
+ /* else if (table->operation == INSERT) */
+ sqlite3_reset(insert_stmt);
+
+ ret = sqlite3_bind_text(insert_stmt, 1, table->appid, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind appid : %s for preparing statement", table->appid);
+
+ ret = sqlite3_bind_text(insert_stmt, 2, table->pkgid, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind pkgid : %s for preparing statement", table->pkgid);
+
+ ret = sqlite3_bind_int(insert_stmt, 3, table->time);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind time : %d for preparing statement", table->time);
+
+ ret = sqlite3_bind_text(insert_stmt, 4, table->data, -1, SQLITE_STATIC);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind data : %s for preparing statement", table->data);
+
+ ret = sqlite3_bind_int(insert_stmt, 5, index++);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_OK, _SE, "Can not bind index : %d for preparing statement", index);
+
+ ret = sqlite3_step(insert_stmt);
+ FINALIZE_AND_RETURN_IF(ret != SQLITE_DONE, _E, "insert %s table failed %s", module->name, sqlite3_errmsg(module->db));
+ }
+
+ table = g_queue_pop_head(module->cache);
+ free(table);
+ }
+ pthread_mutex_unlock(&(module->cache_mutex));
+ sqlite3_exec(module->db, "COMMIT;", NULL, NULL, NULL);
+ sqlite3_finalize(insert_stmt);
+ insert_stmt = NULL;
+ sqlite3_finalize(delete_stmt);
+ delete_stmt = NULL;
+ }
+
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules, struct logging_module *, i);
+
+ /* Check storage limitation by maximum period and storage size (50MiB) */
+ if (logging_check_storage_size(module->db_path) == RESOURCED_ERROR_FAIL) {
+ logging_reset(module->name);
+ sqlite3_exec(module->db, "VACUUM;", NULL, NULL, NULL);
+ }
+ }
+ }
+
+ static void *logging_data_thread_main(void *arg)
+ {
+ int ret = 0;
+
+ setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
+
+ while (1) {
+ /*
+ * When signalled by main thread,
+ * it starts logging_pthread().
+ */
+ ret = pthread_mutex_lock(&logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&logging_data_cond, &logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&logging_data_mutex);
+ if (ret)
+ _E("logging data thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ logging_save_to_storage(false);
+
+ ret = pthread_mutex_unlock(&logging_data_mutex);
+ if (ret) {
+ _E("logging data thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* Now our thread finishes - cleanup TID */
+ logging_data_thread = 0;
+
+ return NULL;
+ }
+
+ static Eina_Bool logging_send_signal_to_data(void *data)
+ {
+ int ret;
+
+ _D("logging timer callback function start");
+
+ /* signal to logging data thread for start logging */
+ ret = pthread_mutex_trylock(&logging_data_mutex);
+
+ if (ret)
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ else {
+ pthread_cond_signal(&logging_data_cond);
+ _I("send signal to logging data thread");
+ pthread_mutex_unlock(&logging_data_mutex);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ static int logging_start(void *data)
+ {
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static void *logging_update_thread_main(void *arg)
+ {
+ int ret = 0;
+
+ setpriority(PRIO_PROCESS, 0, LOGGING_PTIORITY);
+
+ while (1) {
+ /*
+ * it starts fuction of registered module.
+ */
+ ret = pthread_mutex_lock(&logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ ret = pthread_cond_wait(&logging_update_cond, &logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_cond_wait() failed, %d", ret);
+ ret = pthread_mutex_unlock(&logging_update_mutex);
+ if (ret)
+ _E("logging update thread::pthread_mutex_lock() failed, %d", ret);
+ break;
+ }
+
+ logging_update(false);
+
+ ret = pthread_mutex_unlock(&logging_update_mutex);
+ if (ret) {
+ _E("logging update thread::pthread_mutex_unlock() failed, %d", ret);
+ break;
+ }
+ }
+
+ /* Now our thread finishes - cleanup TID */
+ logging_update_thread = 0;
+
+ return NULL;
+ }
+
+ static Eina_Bool logging_send_signal_to_update(void *data)
+ {
+ int ret;
+ DIR *dir_info;
+
+ dir_info = opendir(LOGGING_FILE_PATH);
+
+ if (dir_info)
+ closedir(dir_info);
+ else {
+ _E("There is no %s", LOGGING_FILE_PATH);
+ ret = mkdir(LOGGING_FILE_PATH, S_IRWXU | S_IRWXG | S_IROTH);
+
+ if (ret) {
+ _E("mkdir failed %s", LOGGING_FILE_PATH);
+ return ECORE_CALLBACK_RENEW;
+ }
+ }
+
+ _D("logging timer callback function start");
+
+ /* signal to logging update thread for start update */
+ ret = pthread_mutex_trylock(&logging_update_mutex);
+
+ if (ret)
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ else {
+ pthread_cond_signal(&logging_update_cond);
+ _I("send signal to logging update thread");
+ pthread_mutex_unlock(&logging_update_mutex);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+ }
+
+ static int logging_thread_create(void)
+ {
+ int ret = RESOURCED_ERROR_NONE;
+
+ /* initialize logging_data_thread */
+ if (logging_data_thread) {
+ _I("logging data thread %u already created", (unsigned)logging_data_thread);
+ } else {
+ ret = pthread_create(&logging_data_thread, NULL,
+ (void *)logging_data_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for logging_data_thread_main failed, %d\n", ret);
+ logging_data_thread = 0;
+ } else {
+ _D("pthread creation for logging data success");
+ pthread_detach(logging_data_thread);
+ }
+ }
+
+ /* initialize logging_update_thread */
+ if (logging_update_thread) {
+ _I("logging update thread %u already created", (unsigned)logging_update_thread);
+ } else {
+ ret = pthread_create(&logging_update_thread, NULL,
+ (void *)logging_update_thread_main, (void *)NULL);
+ if (ret) {
+ _E("pthread creation for logging_update_thread_main failed, %d\n", ret);
+ logging_update_thread = 0;
+ } else {
+ _D("pthread creation for logging update success");
+ pthread_detach(logging_update_thread);
+ }
+ }
+ return ret;
+ }
+
+ static int logging_poweroff(void *data)
+ {
+ /* flush module cache */
+ logging_save_to_storage(true);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_init(void *data)
+ {
+ int ret = RESOURCED_ERROR_NONE;
+ char *err = NULL;
+
+ _D("logging_init start");
+
+ ret = logging_thread_create();
+ if (ret) {
+ _E("logging thread create failed");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ /* module array create */
+ logging_modules = g_array_new(false, false, sizeof(struct logging_module *));
+
+ if (logging_modules == NULL) {
+ _E("logging_modules_array create failed");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* DB create */
+ if (sqlite3_open(LOGGING_DB_FILE_NAME, &logging_db) != SQLITE_OK) {
+ _E("%s DB open failed (%s)", LOGGING_DB_FILE_NAME, sqlite3_errmsg(logging_db));
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = sqlite3_exec(logging_db, "PRAGMA locking_mode = NORMAL", 0, 0, 0);
+ if (ret != SQLITE_OK) {
+ _E("Can't set locking mode %s", sqlite3_errmsg(logging_db));
+ _E("Skip set busy handler.");
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+
+ /* Set how many times we'll repeat our attempts for sqlite_step */
+ if (sqlite3_busy_handler(logging_db, logging_db_busy, NULL) != SQLITE_OK) {
+ _E("Couldn't set busy handler!");
+ }
+
+ options = leveldb_options_create();
+ leveldb_options_set_create_if_missing(options, 1);
+ logging_leveldb = leveldb_open(options, LOGGING_LEVEL_DB_FILE_NAME, &err);
+ if (err != NULL) {
+ _E("Failed to open leveldb");
+ free(err);
+ return RESOURCED_ERROR_DB_FAILED;
+ }
+ roptions = leveldb_readoptions_create();
+ woptions = leveldb_writeoptions_create();
+ leveldb_writeoptions_set_sync(woptions, 1);
+
+ register_notifier(RESOURCED_NOTIFIER_LOGGING_START, logging_start);
+ register_notifier(RESOURCED_NOTIFIER_POWER_OFF, logging_poweroff);
+
+ if (logging_data_timer == NULL) {
+ _D("logging data timer start");
+ logging_data_timer =
+ ecore_timer_add(ONE_MINUTE, logging_send_signal_to_data, (void *)NULL);
+ }
+
+ if (logging_update_timer == NULL) {
+ _D("logging data timer start");
+ logging_update_timer =
+ ecore_timer_add(ONE_MINUTE, logging_send_signal_to_update, (void *)NULL);
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ int logging_exit(void *data)
+ {
+ int i;
+ struct logging_module *module;
+
+ /* update timer delete */
+ ecore_timer_del(logging_update_timer);
+ logging_update_timer = NULL;
+
+ unregister_notifier(RESOURCED_NOTIFIER_LOGGING_START, logging_start);
+ unregister_notifier(RESOURCED_NOTIFIER_POWER_OFF, logging_poweroff);
+
+ /* flush module cache */
+ logging_save_to_storage(true);
+
+ /* logging_modules array deinitialize */
+ for (i = 0; i < logging_modules->len; i++) {
+ module = g_array_index(logging_modules,
+ struct logging_module *, i);
+ free(module->name);
+ free(module->db_path);
+ g_queue_free(module->cache);
+
+ /* DB close */
+ sqlite3_close(module->db);
+ }
+
+ g_array_free(logging_modules, true);
+
+ /* DB close */
+ sqlite3_close(logging_db);
+ if (logging_leveldb)
+ leveldb_close(logging_leveldb);
+ _D("logging_exit");
+
+ return RESOURCED_ERROR_NONE;
+ }
--- /dev/null
- fprintf(stdout, "Try to allocate memory %u", len);
+ /*
+ Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ */
+
+ #include <sys/mman.h>
+ #include <stdio.h>
+ #include <stdbool.h>
+ #include <string.h>
+ #include <errno.h>
+ #include <signal.h>
+
+ #include "util.h"
+ #include "config-parser.h"
+
+ #define MEM_STRESS_CONF "/etc/mem-stress.conf"
+
+ static bool quite = false;
+ static size_t arg_size = 0;
+
+ static int parse_config_file(void)
+ {
+ const ConfigTableItem items[] = {
+ { "MemStress", "Size", config_parse_bytes, 0, &arg_size },
+ { NULL, NULL, NULL, 0, NULL }
+ };
+
+ return config_parse_new(MEM_STRESS_CONF, (void*) items);
+ }
+
+ static void mem_stress_signal_handler(int signo)
+ {
+ if (signo == SIGTERM)
+ quite = true;
+ }
+
+ static int mem_stress_signal_init(void)
+ {
+ if (signal(SIGTERM, &mem_stress_signal_handler) == SIG_ERR) {
+ fprintf(stdout, "Failed to catch SIGTERM signal: %m");
+ return -errno;
+ }
+
+ return 0;
+ }
+
+ static int mem_stress_allocate_memory(void **addr, size_t len)
+ {
+ void *p = NULL;
+ int r;
+
+ do {
- fprintf(stdout, "Memory stress size is: %u", arg_size);
++ fprintf(stdout, "Try to allocate memory %zu", len);
+ p = new0(void, len);
+ } while(!p);
+
+ r = mlock(p, len);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ *addr = p;
+
+ return 0;
+ }
+
+ static int mem_stress_free_memory(void *addr, size_t len)
+ {
+ int r;
+
+ r = munlock(addr, len);
+ if (r < 0)
+ return r;
+
+ free(addr);
+
+ return 0;
+ }
+
+ static int mem_stress_run_loop(void)
+ {
+ void *mem = NULL;
+ int r;
+ char buf[256];
+
++ fprintf(stdout, "Memory stress size is: %zu", arg_size);
+
+ if (!arg_size)
+ return 0;
+
+ r = mem_stress_allocate_memory(&mem, arg_size);
+ if (r < 0) {
+ fprintf(stderr, "Failed to allocate memory: %s", strerror_r(-r, buf, sizeof(buf)));
+ return r;
+ }
+
+ while(!quite)
+ sleep(10);
+
+ r = mem_stress_free_memory(mem, arg_size);
+ if (r < 0) {
+ fprintf(stderr, "Failed to free memory: %s", strerror_r(-r, buf, sizeof(buf)));
+ return r;
+ }
+
+ return 0;
+ }
+
+ int main(int argc, char *argv[])
+ {
+ int r;
+
+ r = access(MEM_STRESS_CONF, F_OK);
+ if (r < 0) {
+ fprintf(stderr, "Failed to access '%s': %m", MEM_STRESS_CONF);
+ return EXIT_FAILURE;
+ }
+
+ r = mem_stress_signal_init();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = parse_config_file();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ r = mem_stress_run_loop();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+ }
static unsigned long totalram;
static unsigned long ktotalram;
-static const struct module_ops memory_modules_ops;
-static const struct module_ops *lowmem_ops;
+ static int fg_killed;
+
++static struct module_ops memory_modules_ops;
++static struct module_ops *lowmem_ops;
+ static bool oom_popup_enable;
+ static bool oom_popup;
- static const struct module_ops memory_modules_ops;
- static const struct module_ops *lowmem_ops;
- static char *memcg_name[MEMCG_MAX] = {
+ static const char *memcg_name[MEMCG_MAX] = {
NULL,
+ "platform",
"foreground",
+ "previous",
+ "favorite",
"background",
"swap",
};
return RESOURCED_ERROR_NONE;
}
--static const struct module_ops memory_modules_ops = {
- .priority = MODULE_PRIORITY_NORMAL,
++static struct module_ops memory_modules_ops = {
+ .priority = MODULE_PRIORITY_EARLY,
.name = "lowmem",
.init = resourced_memory_init,
.exit = resourced_memory_finalize,
-SET(CMAKE_INSTALL_PREFIX /usr)
+ PROJECT(memps)
+
SET(PREFIX ${CMAKE_INSTALL_PREFIX})
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
CLEAN_DIRECT_OUTPUT 1
)
- INSTALL(TARGETS ${PROJECT} DESTINATION lib)
+ INSTALL(FILES ${INCLUDE_PUBLIC_DIR}/data_usage.h DESTINATION include/system)
+ INSTALL(TARGETS ${PROJECT} DESTINATION ${LIB_INSTALL_DIR})
RETURN()
ENDIF()
return RESOURCED_ERROR_NONE;
}
--static const struct module_ops datausage_modules_ops = {
++static struct module_ops datausage_modules_ops = {
.priority = MODULE_PRIORITY_NORMAL,
.name = "datausage",
.init = resourced_datausage_init,
--- /dev/null
- return (gboolean)strstr((char *)user_data, (char *)key);
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ /*
+ * @file proc-appusage.c
+ * @desc check history (application frequency and application execution time) based application usage
+ */
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <glib.h>
+ #include <Ecore.h>
+
+ #include "resourced.h"
+ #include "logging.h"
+ #include "trace.h"
+ #include "module.h"
+ #include "macro.h"
+ #include "proc-main.h"
+ #include "notifier.h"
+ #include "config-parser.h"
+
+ #ifdef HEART_SUPPORT
+ #include "heart-common.h"
+ #endif
+
+ #define UPDATE_INTERVAL DAY_TO_SEC(1)
+ #define APPUSAGE_CONF_FILE "/etc/resourced/proc.conf"
+ #define APPUSAGE_CONF_SECTION "APPUSAGE"
+
+ static int favorite_count;
+ static GHashTable *appusage_favorite_htab;
+
+ #ifdef HEART_SUPPORT
+ static Ecore_Timer *appusage_update_timer;
+ #endif
+
+ static void free_key(gpointer data)
+ {
+ if (data)
+ free(data);
+ }
+
+ static gboolean find_hash(gpointer key, gpointer value, gpointer user_data)
+ {
++ if (!user_data || !key)
++ return FALSE;
++
++ return (strstr((char *)user_data, (char *)key) ? TRUE: FALSE);
+ }
+
+ static void print_favorite_list(gpointer key, gpointer value, gpointer user_data)
+ {
+ _D("favorit app list : %s", (char*)key);
+ }
+
+ #ifdef HEART_SUPPORT
+ static Eina_Bool appusage_update_cb(void *data)
+ {
+ GHashTable *apps_htab = (GHashTable *)data;
+ int ret;
+
+ if (!data)
+ return ECORE_CALLBACK_CANCEL;
+
+ ret = heart_cpu_get_appusage_list(apps_htab, favorite_count);
+ if (!ret) {
+ _I("most_recently_used_list updated");
+ g_hash_table_foreach(apps_htab, print_favorite_list, NULL);
+ }
+
+ return ECORE_CALLBACK_RENEW;
+ }
+ #endif
+
+ static int load_appusage_config(struct parse_result *result, void *user_data)
+ {
+ if(!result)
+ return -EINVAL;
+
+ if (strcmp(result->section, APPUSAGE_CONF_SECTION))
+ return RESOURCED_ERROR_NO_DATA;
+
+ if (!strcmp(result->name, "APPUSAGE")) {
+ if (!strcmp(result->value, "OFF"))
+ return RESOURCED_ERROR_UNINITIALIZED;
+
+ appusage_favorite_htab = g_hash_table_new_full(g_str_hash,
+ g_str_equal, free_key, NULL);
+
+ } else if (!strcmp(result->name, "PREDEFINE")) {
+ g_hash_table_insert(appusage_favorite_htab,
+ g_strndup(result->value, strlen(result->value)), GINT_TO_POINTER(1));
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int proc_appusage_table_init(void)
+ {
+ int ret;
+
+ ret = config_parse(APPUSAGE_CONF_FILE, load_appusage_config, NULL);
+ if(ret || !appusage_favorite_htab)
+ return RESOURCED_ERROR_NO_DATA;
+
+ favorite_count = g_hash_table_size(appusage_favorite_htab);
+
+ #ifdef HEART_SUPPORT
+ ret = heart_cpu_get_appusage_list(appusage_favorite_htab,
+ favorite_count);
+ if (!ret)
+ _I("most_recently_used_list updated");
+ #endif
+
+ g_hash_table_foreach(appusage_favorite_htab, print_favorite_list, NULL);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int booting_done(void *data)
+ {
+ static const struct module_ops *swap;
+ int ret;
+
+ /*
+ * When kernel enables swap feature,
+ * resourced can control favorite applications because it will
+ * apply early swap and late oom kill.
+ */
+ if (!swap) {
+ swap = find_module("swap");
+ if (!swap)
+ return RESOURCED_ERROR_NO_DATA;
+ }
+
+ ret = proc_appusage_table_init();
+ if (ret)
+ return RESOURCED_ERROR_NO_DATA;
+
+ #ifdef HEART_SUPPORT
+ appusage_update_timer = ecore_timer_add(UPDATE_INTERVAL,
+ appusage_update_cb, (void *)appusage_favorite_htab);
+ #endif
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ bool proc_check_favorite_app(char *appid)
+ {
+ gpointer app_ptr = NULL;
+
+ if (!appusage_favorite_htab)
+ return false;
+
+ app_ptr = g_hash_table_find(appusage_favorite_htab,
+ find_hash, (gpointer)appid);
+ if (app_ptr)
+ return true;
+ return false;
+ }
+
+ static int proc_appusage_init(void *data)
+ {
+ register_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int proc_appusage_exit(void *data)
+ {
+ if (appusage_favorite_htab)
+ g_hash_table_destroy(appusage_favorite_htab);
+ unregister_notifier(RESOURCED_NOTIFIER_BOOTING_DONE, booting_done);
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static const struct proc_module_ops proc_appusage_ops = {
+ .name = "PROC_APPUSAGE",
+ .init = proc_appusage_init,
+ .exit = proc_appusage_exit,
+ };
+ PROC_MODULE_REGISTER(&proc_appusage_ops)
--- /dev/null
- int server_fd, client_fd, client_len;
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ /*
+ * @file proc_info.c
+ * @desc It's main thread to get system & process information
+ * to provide runtime api
+ */
+
+ #include <stdio.h>
+ #include <limits.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ #include <sys/stat.h>
+ #include <sys/stat.h>
+ #include <sys/xattr.h>
+ #include <pthread.h>
+
+ #include <Ecore.h>
+
+ #include "macro.h"
+ #include "proc-main.h"
+ #include "proc-info.h"
+ #include "proc-process.h"
+ #include "proc-noti.h"
+ #include "resourced.h"
+ #include "module.h"
+ #include "trace.h"
+
+ #define MAX_CONNECTION 5
+
+ static pthread_t proc_info_thread;
+
+ static void *resourced_proc_info_func(void *data)
+ {
+ struct sockaddr_un client_address;
+ struct resourced_noti *msg;
+ char *send_buffer = NULL;
+ int ret, send_len = 0;
- server_fd = (int)data;
++ long server_fd, client_fd, client_len;
+ pid_t pid;
+ struct timeval tv = { 1, 0 }; /* 1 sec */
+
+ if (!data) {
+ _E("data is NULL");
+ return NULL;
+ }
+
- int fd, ret;
++ server_fd = (long)data;
+
+ client_len = sizeof(client_address);
+
+ if (listen(server_fd, MAX_CONNECTION) < 0) {
+ _E("Failed to listen socket");
+ close(server_fd);
+ return NULL;
+ }
+
+ while (1) {
+ client_fd =
+ accept(server_fd, (struct sockaddr *)&client_address,
+ (socklen_t *)&client_len);
+ if (client_fd < 0) {
+ _E("Failed to accept");
+ continue;
+ }
+ msg = calloc(1, sizeof(struct resourced_noti));
+ if (msg == NULL) {
+ _E("proc_noti_cb : Not enough memory");
+ goto end;
+ }
+ ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret)
+ _E("failed to set socket option");
+ ret = read_message(client_fd, msg);
+ if (ret)
+ goto end;
+ if (msg->argc > NOTI_MAXARG || msg->type < PROC_CGROUP_GET_CMDLINE) {
+ _E("%s : error argument", __func__);
+ goto end;
+ }
+
+ pid = atoi(msg->argv[0]);
+ send_len = atoi(msg->argv[1]);
+ send_buffer = calloc(1, send_len);
+ if (send_buffer == NULL) {
+ _E("Not enough memory");
+ goto end;
+ }
+ ret = proc_get_state(msg->type, pid, send_buffer, send_len);
+ end:
+ write_response(&ret, client_fd, send_buffer, send_len);
+ free_message(msg);
+ close(client_fd);
+ if (send_buffer) {
+ free(send_buffer);
+ send_buffer = NULL;
+ }
+ ret = 0;
+ send_len = 0;
+ }
+
+ return NULL;
+ }
+
+ static int proc_info_socket_init(void)
+ {
+ int fd;
+ struct sockaddr_un serveraddr;
+
+ if (access(RESOURCED_PROC_INFO_SOCKET_PATH, F_OK) == 0)
+ unlink(RESOURCED_PROC_INFO_SOCKET_PATH);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ _E("Failed to create socket");
+ return -1;
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPOUT", "@", 2, 0)) < 0) {
+ _E("Failed to set Socket SMACK label");
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ if ((fsetxattr(fd, "security.SMACK64IPIN", "*", 2, 0)) < 0) {
+ _E("Failed to set Socket SMACK label");
+ if (errno != EOPNOTSUPP) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ bzero(&serveraddr, sizeof(struct sockaddr_un));
+ serveraddr.sun_family = AF_UNIX;
+ strncpy(serveraddr.sun_path, RESOURCED_PROC_INFO_SOCKET_PATH,
+ sizeof(serveraddr.sun_path));
+
+ if (bind(fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)) < 0) {
+ _E("Failed to bind socket");
+ close(fd);
+ return -1;
+ }
+
+ if (chmod(RESOURCED_PROC_INFO_SOCKET_PATH, (S_IRWXU | S_IRWXG | S_IRWXO)) < 0)
+ _E("Failed to change the socket permission");
+ _D("socket create ok");
+ return fd;
+ }
+
+ static int proc_info_init(void *data)
+ {
++ int ret;
++ long fd;
+
+ fd = proc_info_socket_init();
+ if (fd < 0) {
+ _E("Failed to init socket");
+ return -1;
+ }
+ /* start thread */
+ ret = pthread_create(&proc_info_thread, NULL, resourced_proc_info_func,
+ (void *)fd);
+ if (ret != 0) {
+ _E("Failed to create thread");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int proc_info_exit(void *data)
+ {
+ _D("proc info exit!");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static const struct proc_module_ops proc_info_ops = {
+ .name = "PROC_INFO",
+ .init = proc_info_init,
+ .exit = proc_info_exit,
+ };
+ PROC_MODULE_REGISTER(&proc_info_ops)
static gboolean find_excluded(gpointer key, gpointer value, gpointer user_data)
{
- if(!user_data || !key)
- return (gboolean)strstr((char *)user_data, (char *)key);
++ if (!user_data || !key)
+ return FALSE;
+
- return (strstr((char*)user_data, (char*)key) ? TRUE : FALSE);
++ return (strstr((char *)user_data, (char *)key) ? TRUE : FALSE);
+ }
+
+ int proc_get_id_info(struct proc_status *ps, char **app_name, char **pkg_name)
+ {
+ if (!ps)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (!ps->pai || !ps->pai->ai) {
+ *app_name = TIZEN_SYSTEM_APPID;
+ *pkg_name = TIZEN_SYSTEM_APPID;
+ } else {
+ *app_name = ps->pai->ai->appid;
+ *pkg_name = ps->pai->ai->pkgname;
+ }
+ return RESOURCED_ERROR_NONE;
+ }
+
+ char *proc_get_appid_from_pid(const pid_t pid)
+ {
+ struct proc_app_info *pai = find_app_info(pid);
+ if (!pai)
+ return NULL;
+ return pai->appid;
}
int resourced_proc_excluded(const char *app_name)
f = fopen(buf, "w+");
}
proc_dump_process_list(f);
- modules_dump((void*)f, mode);
- if (f)
- fclose(f);
+ modules_dump((void *)f, mode);
}
-static const struct module_ops proc_modules_ops = {
+
++static struct module_ops proc_modules_ops = {
+ .priority = MODULE_PRIORITY_EARLY,
+ .name = "PROC",
+ .init = resourced_proc_init,
+ .exit = resourced_proc_exit,
+ };
+
+ MODULE_REGISTER(&proc_modules_ops)
--- /dev/null
-static const struct module_ops resourced_dbus_modules_ops = {
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ #include <assert.h>
+ #include <dbus/dbus.h>
+ #include <glib.h>
+ #include <E_DBus.h>
+
+ #include "edbus-handler.h"
+ #include "macro.h"
+ #include "util.h"
+ #include "module.h"
+ #include "resourced.h"
+ #include "trace.h"
+ #include "resourced-dbus.h"
+
+ /* key : DBus system BusName */
+ /* value: PID of Busname */
+ /* BusName hash is 1:1 table */
+ static GHashTable *dbus_system_busname_hash = NULL;
+
+ /* key : PID */
+ /* value: Another GHashTable of BusNames */
+ /* BusName hash is 1:N table */
+ static GHashTable *dbus_system_pid_hash = NULL;
+
+ E_DBus_Signal_Handler *dbus_name_owner_changed_handler = NULL;
+
+
+ static void ghash_free(void *hash)
+ {
+ if (g_hash_table_size((GHashTable *)hash))
+ g_hash_table_remove_all((GHashTable *)hash);
+
+ g_hash_table_unref((GHashTable *)hash);
+ }
+
+ bool resourced_dbus_pid_has_busname(pid_t pid)
+ {
+ return g_hash_table_contains(dbus_system_pid_hash, GUINT_TO_POINTER(pid));
+ }
+
+ unsigned int resourced_dbus_pid_get_busnames(pid_t pid, char ***busnames)
+ {
+ void *pid_p = GUINT_TO_POINTER(pid);
+ GHashTable *busname_hash = NULL;
+ char **names = NULL;
+ unsigned int len = 0;
+
+ assert(busnames);
+
+ busname_hash = g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash)
+ return 0;
+
+ #if (GLIB_MAJOR_VERSION <= 2 && GLIB_MINOR_VERSION < 40)
+ {
+ GHashTableIter iter;
+ char *busname;
+ int i;
+
+ names = (char **)malloc(sizeof(char *) * (size_t)(g_hash_table_size(busname_hash) + 1));
+ if (!names)
+ return 0;
+
+ g_hash_table_iter_init (&iter, busname_hash);
+ for (i = 0; g_hash_table_iter_next(&iter, (void **)&busname, NULL); i++)
+ names[i] = strndup(busname, strlen(busname)+1);
+
+ names[i] = NULL;
+ }
+ #else
+ names = (char **)g_hash_table_get_keys_as_array(busname_hash, &len);
+ #endif
+ *busnames = names;
+
+ return len;
+ }
+
+ pid_t resourced_dbus_get_pid_of_busname(const char *busname)
+ {
+ return GPOINTER_TO_UINT(g_hash_table_lookup(dbus_system_busname_hash, busname));
+ }
+
+ static void resourced_dbus_system_bus_append_connection_info(void *key, void *value, void *data)
+ {
+ DBusMessageIter *iter = data, sub;
+ char *busname = key;
+ pid_t pid = GPOINTER_TO_UINT(value);
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub)) {
+ _E("Failed to open dictionary");
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &busname)) {
+ _E("Failed to append string: %s", busname);
+ dbus_message_iter_abandon_container(iter, &sub);
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &pid)) {
+ _E("Failed to append uint32: %u", pid);
+ dbus_message_iter_abandon_container(iter, &sub);
+ return;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub)) {
+ _E("Failed to close array");
+ return;
+ }
+ }
+
+ static DBusMessage *resourced_dbus_handle_get_system_bus_name_info(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ DBusMessageIter iter, sub;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{su}", &sub)) {
+ _E("Failed to open string array");
+ return NULL;
+ }
+
+ g_hash_table_foreach(dbus_system_busname_hash,
+ resourced_dbus_system_bus_append_connection_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+ }
+
+ static void resourced_dbus_system_bus_append_busname_info(void *key, void *value, void *data)
+ {
+ if (!dbus_message_iter_append_basic((DBusMessageIter *)data, DBUS_TYPE_STRING, &key))
+ _E("Failed to append string: %s", (char *)key);
+ }
+
+ static DBusMessage *resourced_dbus_handle_get_system_bus_pid_info(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ GHashTable *busname_hash = NULL;
+ DBusMessageIter iter, sub;
+ DBusMessage *reply = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+ pid_t pid;
+
+ if (!dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) {
+ _E("Failed to get arguments from message: %s", error.message);
+ return NULL;
+ }
+
+ if (!g_hash_table_contains(dbus_system_pid_hash, GUINT_TO_POINTER(pid))) {
+ reply = dbus_message_new_error(msg,
+ DBUS_ERROR_INVALID_ARGS,
+ "Given PID has no system bus");
+ return reply;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ busname_hash = g_hash_table_lookup(dbus_system_pid_hash, GUINT_TO_POINTER(pid));
+ if (!busname_hash) {
+ _E("Failed to find value of key(PID: %u)", pid);
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ _E("Failed to open string array");
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ g_hash_table_foreach(busname_hash,
+ resourced_dbus_system_bus_append_busname_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+ }
+
+ static void resourced_dbus_system_bus_append_pid_info(void *key, void *value, void *data)
+ {
+ DBusMessageIter *iter = data, sub, ssub;
+ pid_t pid = GPOINTER_TO_UINT(key);
+ GHashTable *busname_hash = value;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub)) {
+ _E("Failed to open dictionary");
+ return;
+ }
+
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &pid)) {
+ _E("Failed to append uint32: %u", pid);
+ return;
+ }
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "s", &ssub)) {
+ _E("Failed to open string array");
+ return;
+ }
+
+ g_hash_table_foreach(busname_hash,
+ resourced_dbus_system_bus_append_busname_info,
+ &ssub);
+
+ if (!dbus_message_iter_close_container(&sub, &ssub)) {
+ _E("Failed to close array");
+ return;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &sub)) {
+ _E("Failed to close array");
+ return;
+ }
+ }
+
+ static DBusMessage *resourced_dbus_handle_get_system_bus_pid_info_all(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ DBusMessageIter iter, sub;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ _E("Failed to create method reply");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{uas}", &sub)) {
+ _E("Failed to open string array");
+ dbus_message_unref (reply);
+ return NULL;
+ }
+
+ g_hash_table_foreach(dbus_system_pid_hash,
+ resourced_dbus_system_bus_append_pid_info,
+ &sub);
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return NULL;
+ }
+
+ return reply;
+ }
+
+ static struct edbus_method resourced_dbus_methods[] = {
+ { "GetSystemBusPIDInfo", "u", "as",
+ resourced_dbus_handle_get_system_bus_pid_info },
+ { "GetSystemBusPIDInfoAll", NULL, "a{uas}",
+ resourced_dbus_handle_get_system_bus_pid_info_all },
+ { "GetSystemBusNameInfo", NULL, "a{su}",
+ resourced_dbus_handle_get_system_bus_name_info },
+ { NULL, NULL, NULL,
+ NULL },
+ };
+
+ static void resourced_dbus_system_hash_insert_busname(char *busname, pid_t pid)
+ {
+ GHashTable *busname_hash = NULL;
+ void *pid_p = GUINT_TO_POINTER(pid);
+
+ g_hash_table_replace(dbus_system_busname_hash, busname, pid_p);
+
+ busname_hash = (GHashTable *)g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash) {
+ /* !! CAUTION !! */
+ /* We are using same busname pointer at two of hash
+ * table. So the free operation should be done at
+ * once. */
+ busname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+ if (!busname_hash) {
+ _E("Failed to create BusName hash table");
+ return;
+ }
+ g_hash_table_replace(dbus_system_pid_hash, pid_p, busname_hash);
+ }
+
+ g_hash_table_replace(busname_hash, busname, NULL);
+ }
+
+ static void resourced_dbus_system_hash_drop_busname(char *busname)
+ {
+ GHashTable *busname_hash = NULL;
+ void *pid_p;
+
+ /* BusName hash */
+ if (!g_hash_table_contains(dbus_system_busname_hash, busname)) {
+ _E("Does not exist in busname hash: %s", busname);
+ return;
+ }
+
+ /* Lookup PID of BusName */
+ pid_p = g_hash_table_lookup(dbus_system_busname_hash, busname);
+
+ /* Drop PID hash */
+ if (!g_hash_table_contains(dbus_system_pid_hash, pid_p)) {
+ _E("Does not exist in PID hash: %s", busname);
+ return;
+ }
+
+ busname_hash = (GHashTable *)g_hash_table_lookup(dbus_system_pid_hash, pid_p);
+ if (!busname_hash) {
+ _E("Failed to find value of PID: %u", GPOINTER_TO_UINT(pid_p));
+ g_hash_table_remove(dbus_system_pid_hash, pid_p);
+ return;
+ }
+
+ g_hash_table_remove(busname_hash, busname);
+
+ if (!g_hash_table_size(busname_hash)) {
+ g_hash_table_unref(busname_hash);
+ if (!g_hash_table_remove(dbus_system_pid_hash, pid_p))
+ _E("Failed to drop from PID hash table: %s", busname);
+ }
+
+ /* Drop BusName hash */
+ if (!g_hash_table_remove(dbus_system_busname_hash, busname))
+ _E("Failed to drop from busname hash table: %s", busname);
+ }
+
+ static void resourced_dbus_get_connection_unix_process_id_callback(void *data, DBusMessage *msg, DBusError *error)
+ {
+ DBusError err = DBUS_ERROR_INIT;
+ char *busname = data;
+ pid_t pid;
+
+ if (dbus_error_is_set(error)) {
+ free(busname);
+ return;
+ }
+
+ if (!dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) {
+ free(busname);
+ _E("Failed to get arguments from message: %s", err.message);
+ return;
+ }
+
+ resourced_dbus_system_hash_insert_busname(busname, pid);
+ }
+
+ static void resourced_dbus_get_connection_unix_process_id(char *busname)
+ {
+ E_DBus_Connection *conn = NULL;
+ DBusMessage *msg = NULL;
+ DBusPendingCall *pending = NULL;
+
+ conn = get_resourced_edbus_connection();
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionUnixProcessID");
+ if (!msg) {
+ _E("Failed to get new message");
+ return;
+ }
+
+ if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &busname, DBUS_TYPE_INVALID)) {
+ _E("Failed to append args");
+ dbus_message_unref(msg);
+ return;
+ }
+
+ pending = e_dbus_message_send(conn,
+ msg,
+ resourced_dbus_get_connection_unix_process_id_callback,
+ -1,
+ strndup(busname, strlen(busname)+1));
+ if (!pending) {
+ _E("Failed to send message");
+ dbus_message_unref(msg);
+ return;
+ }
+
+ dbus_message_unref(msg);
+
+ return;
+ }
+
+ static void resourced_dbus_get_list_names_callback(void *data, DBusMessage *msg, DBusError *error)
+ {
+ DBusMessageIter iter, sub;
+ char *busname = NULL;
+
+ if (dbus_error_is_set(error)) {
+ _E("Failed to get DBus list names: %s", error->message);
+ return;
+ }
+
+ if (!dbus_message_iter_init(msg, &iter)) {
+ _D("message has no arguments");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+ do {
+ dbus_message_iter_get_basic(&sub, &busname);
+ resourced_dbus_get_connection_unix_process_id(busname);
+ } while(dbus_message_iter_next(&sub));
+ }
+
+ static bool resourced_dbus_get_list_names(void)
+ {
+ DBusMessage *msg = NULL;
+ DBusPendingCall *pending = NULL;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "ListNames");
+ if (!msg) {
+ _E("Failed to get new message");
+ return FALSE;
+ }
+
+ pending = e_dbus_message_send(get_resourced_edbus_connection(),
+ msg,
+ resourced_dbus_get_list_names_callback,
+ -1,
+ NULL);
+ if (!pending) {
+ _E("Failed to send message");
+ dbus_message_unref(msg);
+ return FALSE;
+ }
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+ }
+
+ static bool resourced_dbus_become_monitor(DBusConnection *connection,
+ const char * const *filters)
+ {
+ DBusError error = DBUS_ERROR_INIT;
+ DBusMessage *msg;
+ DBusMessage *reply;
+ int i;
+ unsigned int zero = 0;
+ DBusMessageIter iter, sub;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_MONITORING,
+ "BecomeMonitor");
+ if (!msg) {
+ _E("Failed to become a monitor");
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ _E("Failed to open string array");
+ return FALSE;
+ }
+
+ for (i = 0; filters[i] != NULL; i++) {
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &filters[i])) {
+ _E("Failed to add filter to array");
+ return FALSE;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ _E("Failed to close array");
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &zero)) {
+ _E("Failed to append finish argument zero");
+ return FALSE;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error);
+ if (!reply) {
+ _E("Failed to enable new-style monitoring: "
+ "%s: \"%s\". Falling back to eavesdropping.",
+ error.name, error.message);
+ dbus_error_free(&error);
+ return FALSE;
+ }
+
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+
+ return (reply != NULL);
+ }
+
+ E_DBus_Connection *resourced_dbus_monitor_new(DBusBusType type,
+ DBusHandleMessageFunction filter_func,
+ const char * const *filters)
+ {
+ E_DBus_Connection *edbus_conn = NULL;
+ DBusConnection *conn = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+
+ conn = dbus_bus_get_private(type, &error);
+ if (!conn) {
+ _E("Failed to open connecion: %s", error.message);
+ goto on_error;
+ }
+
+ if (!dbus_connection_add_filter(conn,
+ filter_func,
+ NULL,
+ NULL)) {
+ _E("Failed to add filter function on connection");
+ goto on_error;
+ }
+
+ if (!resourced_dbus_become_monitor(conn, filters)) {
+ _E("Failed to become a monitor connection");
+ goto on_error;
+ }
+
+ edbus_conn = e_dbus_connection_setup(conn);
+ if (!edbus_conn) {
+ _E("Failed to setup edbus connection");
+ goto on_error;
+ }
+
+ return edbus_conn;
+
+ on_error:
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+
+ if (conn) {
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ }
+
+ return NULL;
+ }
+
+ static void resourced_dbus_name_owner_changed_callback(void *data, DBusMessage *msg)
+ {
+ char *busname = NULL, *from = NULL, *to = NULL;
+ DBusError error = DBUS_ERROR_INIT;
+
+ if (!dbus_message_get_args(msg, &error,
+ DBUS_TYPE_STRING, &busname,
+ DBUS_TYPE_STRING, &from,
+ DBUS_TYPE_STRING, &to,
+ DBUS_TYPE_INVALID)) {
+ _E("Failed to get arguments from message: %s", error.message);
+ return;
+ }
+
+ /* If the name owner process is activated then: */
+ /* arg_0: busname */
+ /* arg_1: "" */
+ /* arg_2: ":x.xxx" */
+
+ /* If the name owner process is deactivated then: */
+ /* arg_0: busname */
+ /* arg_1: ":x.xxx" */
+ /* arg_2: "" */
+
+ if (is_empty(busname)) {
+ _E("NameOwnerChanged: arg_0 is empty");
+ return;
+ }
+
+ if (is_empty(from) && is_empty(to)) {
+ _E("NameOwnerChanged: both arg_1 and arg_2 are empty");
+ return;
+ }
+
+ if (is_empty(from) && !is_empty(to)) {
+ /* New BusName */
+
+ resourced_dbus_get_connection_unix_process_id(busname);
+ return;
+ } else if (!is_empty(from) && is_empty(to)) {
+ /* Drop BusName */
+
+ resourced_dbus_system_hash_drop_busname(busname);
+ return;
+ }
+
+ _E("Should NOT reached here!!");
+ }
+
+ static int resourced_dbus_init(void *data)
+ {
+ resourced_ret_c ret;
+
+ dbus_system_busname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
+ if (!dbus_system_busname_hash)
+ _E("Failed to create dbus connection list hash table");
+
+ dbus_system_pid_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, ghash_free);
+ if (!dbus_system_pid_hash)
+ _E("Failed to create dbus connection list hash table");
+
+ resourced_dbus_get_list_names();
+
+ dbus_name_owner_changed_handler = e_dbus_signal_handler_add(get_resourced_edbus_connection(),
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged",
+ resourced_dbus_name_owner_changed_callback,
+ NULL);
+
+ ret = edbus_add_methods(RESOURCED_PATH_DBUS,
+ resourced_dbus_methods,
+ ARRAY_SIZE(resourced_dbus_methods));
+
+ ret_value_msg_if(ret != RESOURCED_ERROR_NONE, ret,
+ "DBus method registration for %s is failed", RESOURCED_PATH_DBUS);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int resourced_dbus_finalize(void *data)
+ {
+ ghash_free(dbus_system_busname_hash);
+ ghash_free(dbus_system_pid_hash);
+
+ return RESOURCED_ERROR_NONE;
+ }
+
++static struct module_ops resourced_dbus_modules_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "resourced-dbus",
+ .init = resourced_dbus_init,
+ .exit = resourced_dbus_finalize,
+ };
+
+ MODULE_REGISTER(&resourced_dbus_modules_ops)
--- /dev/null
-EnvironmentFile=/run/tizen-mobile-env
+ [Unit]
+ Description=Resource management daemon
+ After=wm_ready.service
+
+ [Service]
+ Type=notify
+ ExecStart=/usr/bin/resourced
+ MemoryLimit=30M
+ Restart=on-failure
+ RestartSec=0
+
+ [Install]
+ WantedBy=multi-user.target
--- /dev/null
-static void sluggish_launch_popup(int type)
+ /*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ /**
+ * @file sluggish.c
+ * @desc Sluggishness processing functions
+ *
+ **/
+
+ #include <glib.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <stdlib.h>
+ #include "edbus-handler.h"
+ #include "trace.h"
+ #include "module.h"
+ #include "macro.h"
+ #include "time-helper.h"
+ #include "proc-cpu.h"
+ #include "storage-helper.h"
+ #include "file-helper.h"
+ #include "util.h"
+
+ #define SLUGGISH_PATH "/opt/usr/share/sluggish" /* Path to dump system snapshot */
+ #define PATH_DLOG "/opt/var/log/dlog_main" /* Platform log file */
+ #define PATH_DLOG1 "/opt/var/log/dlog_main.1" /* Platform log file */
+ #define MAX_TIMESTAMP_LEN 30
+ #define MAX_FILENAME_LEN 256
+ #define MAX_BUF_LEN 1024
+ #define POPUP_KEY_CONTENT "_SYSPOPUP_CONTENT_"
+ #define SLUGGISH_POPUP "sluggish_popup"
+ #define SLUGGISH_TYPE "_DETECTION_TYPE_"
+ #define MEMINFO_FILE_PATH "/proc/meminfo" /* meminfo file path */
+ #define VMSTAT_FILE_PATH "/proc/vmstat" /* vmstat file path */
+ #define SLUGGISH_DUP_NOTI_TIMELIMIT 5 /* duplicate sluggish notification timelimit */
+ #define KILOBYTE 1024
+ #define POPUP_DELAY_TIME 15 /* Unit: seconds */
+ #define CPU_THRESHOLD 80 /* Delay popup if CPU usage percentage crossed this limit */
+ #define GET_BATTERY_CAPACITY "GetPercent"
+
+ static int sluggish_count; /* sluggish notification count*/
+
+ enum sluggish_event {
+ GRAPHICS = 1, /* Graphics vertical */
+ MULTIMEDIA /* Multimedia vertical */
+ };
+
+ typedef void (*sluggish_stat_func)(char *ts);
+
+ struct sluggish_stat {
+ char *type;
+ sluggish_stat_func func;
+ };
+
- sluggish_launch_popup((int )user_data);
++static void sluggish_launch_popup(long type)
+ {
+ char *param[4];
+ int ret;
+
+ /* Launch sluggish system popup */
+ param[0] = POPUP_KEY_CONTENT;
+ param[1] = SLUGGISH_POPUP;
+ param[2] = SLUGGISH_TYPE;
+ if (type == GRAPHICS)
+ param[3] = "Graphics sluggishenss";
+ else if (type == MULTIMEDIA)
+ param[3] = "Multimedia sluggishens";
+ else {
+ _E("Invalid sluggish type :%d", type);
+ return;
+ }
+ ret = dbus_method_async("org.tizen.system.popup",
+ "/Org/Tizen/System/Popup/System",
+ "org.tizen.system.popup.System",
+ "SluggishPopupLaunch", "ssss", param);
+ if (ret < 0)
+ _E("Failed to launch SluggishPopup");
+ else
+ _I("SluggishPopupLaunch Success");
+ }
+
+ static gboolean sluggish_popup_cb(gpointer user_data)
+ {
-static void sluggish_get_sys_status(char *timestamp, int slug_vertical, int pid)
++ sluggish_launch_popup((long)user_data);
+ return FALSE;
+ }
+
+ static void sluggish_get_vmstat(char *timestamp)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/vmstat", SLUGGISH_PATH, timestamp);
+ /* dump /proc/vmstat to SLUGGISH_PATH */
+ ret = copy_file(file_name, VMSTAT_FILE_PATH);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", VMSTAT_FILE_PATH, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", VMSTAT_FILE_PATH, file_name);
+ }
+
+ static void sluggish_get_memps(char *timestamp)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+ char *argv[3];
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/memps", SLUGGISH_PATH, timestamp);
+ argv[0] = "memps";
+ argv[1] = "-a";
+ argv[2] = NULL;
+
+ /* dump memps output to SLUGGISH_PATH */
+ ret = exec_cmd(argv, file_name);
+ if (ret < 0)
+ _E("Cmd: memps -a Failed");
+ else
+ _I("Cmd: memps -a Success");
+ }
+
+ static void sluggish_get_meminfo(char *timestamp)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/meminfo", SLUGGISH_PATH, timestamp);
+ /* dump /proc/meminfo to SLUGGISH_PATH */
+ ret = copy_file(file_name, MEMINFO_FILE_PATH);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", MEMINFO_FILE_PATH, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", MEMINFO_FILE_PATH, file_name);
+ }
+
+ static void sluggish_get_psinfo(char *timestamp)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ int ret;
+ char *argv[4];
+
+ snprintf(file_name, sizeof(file_name), "%s/%s/ps-eo", SLUGGISH_PATH, timestamp);
+ argv[0] = "ps";
+ argv[1] = "-eo";
+ argv[2] = "pid,uname,ppid,pri,ni,vsize,rss,pcpu,pmem,size,time,s,policy,cmd";
+ argv[3] = NULL;
+
+ /* dump ps -eo output to SLUGGISH_PATH */
+ ret = exec_cmd(argv, file_name);
+ if (ret < 0)
+ _E("Cmd: ps -eo Failed");
+ else
+ _I("Cmd: ps -eo Success");
+ }
+
+ static const struct sluggish_stat sluggish_memstat[] = {
+ { "vmstat", sluggish_get_vmstat },
+ { "memps", sluggish_get_memps },
+ { "meminfo", sluggish_get_meminfo },
+ };
+
+ static void sluggish_get_mem_status(char *timestamp)
+ {
+ /* Get memory status using vmstat, meminfo, memps and dump to SLUGGISH_PATH */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sluggish_memstat); i++) {
+ sluggish_memstat[i].func(timestamp);
+ }
+ }
+
+ static void sluggish_get_dlog(char *timestamp)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ char file_name1[MAX_FILENAME_LEN];
+ int ret;
+
+ /* Dump platform log files to SLUGGISH_PATH */
+ if (!access(PATH_DLOG, F_OK)) {
+ snprintf(file_name, sizeof(file_name), "%s/%s/dlog_main", SLUGGISH_PATH, timestamp);
+ ret = copy_file(file_name, PATH_DLOG);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", PATH_DLOG, file_name);
+ else
+ _I("Copy: %s to %s SUCCESS", PATH_DLOG, file_name);
+ }
+ if (!access(PATH_DLOG1, F_OK)) {
+ snprintf(file_name1, sizeof(file_name1), "%s/%s/dlog_main.1", SLUGGISH_PATH, timestamp);
+ ret = copy_file(file_name1, PATH_DLOG1);
+ if (ret < 0)
+ _E("Copy: %s to %s FAILED", PATH_DLOG1, file_name1);
+ else
+ _I("Copy: %s to %s SUCCESS", PATH_DLOG1, file_name1);
+ }
+ }
+
+ static int sluggish_get_battery_status(void)
+ {
+ int capacity, ret;
+ DBusMessage *msg;
+
+ /* Get battery status from deviced */
+ msg = dbus_method_sync(DEVICED_BUS_NAME, DEVICED_PATH_BATTERY,
+ DEVICED_INTERFACE_BATTERY,
+ GET_BATTERY_CAPACITY,
+ NULL, NULL);
+ if (!msg) {
+ _E("Failed to sync DBUS message.");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ ret = dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &capacity, DBUS_TYPE_INVALID);
+ dbus_message_unref(msg);
+ if (!ret) {
+ _E("Failed: dbus_message_get_args()");
+ return RESOURCED_ERROR_FAIL;
+ }
+ return capacity;
+ }
+
+ static void sluggish_get_summary(char *timestamp, int slug_vertical, int pid, double cpu_usage)
+ {
+ char file_name[MAX_FILENAME_LEN];
+ char buf[MAX_BUF_LEN] = "";
+ char batbuf[MAX_BUF_LEN] = "";
+ char proc[MAX_FILENAME_LEN];
+ struct statvfs s;
+ double int_tot = 0;
+ double int_free = 0;
+ double ext_tot = 0;
+ double ext_free = 0;
+ FILE *fptr = NULL;
+ FILE *fp = NULL;
+ int batteryp;
+ size_t size;
+
+ /*
+ * Dump
+ * 1. Storage status
+ * 2. Battery status
+ * 3. Sluggishness type
+ * 4. Sluggish PID/Process
+ * 5. CPU Usage
+ */
+
+ /* Get internal memory status */
+ memset(&s, 0x00, sizeof(struct statvfs));
+ if (storage_get_size(INTERNAL, &s) < 0 ) {
+ _E("Fail to get internal memory size");
+ } else {
+ int_tot = ((double)s.f_frsize * s.f_blocks) / KILOBYTE;
+ int_free = ((double)s.f_bsize * s.f_bavail) / KILOBYTE;
+ _I("Internal Memory Status:Total : %lfKB, Avail : %lfKB", int_tot, int_free);
+ }
+
+ /* Get external memory status */
+ memset(&s, 0x00, sizeof(struct statvfs));
+ if (storage_get_size(EXTERNAL, &s) < 0 ) {
+ _E("Fail to get external memory size");
+ } else {
+ ext_tot = ((double)s.f_frsize * s.f_blocks) / KILOBYTE;
+ ext_free = ((double)s.f_bsize * s.f_bavail) / KILOBYTE;
+ _I("External Memory Status:Total : %lfKB, Avail : %lfKB", ext_tot, ext_free);
+ }
+ snprintf(file_name, sizeof(file_name), "%s/%s/summary", SLUGGISH_PATH, timestamp);
+ if (ext_tot > 0) {
+ snprintf(buf, sizeof(buf), "Internal Memory Status:\nTotal : %lfKB, Avail : %lfKB\nExternal Memory Status:\nTotal : %lfKB, Avail : %lfKB\n",
+ int_tot, int_free, ext_tot, ext_free);
+ } else
+ snprintf(buf, sizeof(buf), "Internal Memory Status:\nTotal : %lfKB, Avail : %lfKB\n", int_tot, int_free);
+
+
+ /* Get Battery status */
+ batteryp = sluggish_get_battery_status();
+ if (batteryp >= 0)
+ snprintf(batbuf, sizeof(batbuf), "Battery percentage:%d%%\n", batteryp);
+
+ /* Open file in SLUGGISH_PATH to dump summary */
+ fptr = fopen(file_name, "w+");
+ if (fptr == NULL) {
+ _E("Failed to open file %s", file_name);
+ return;
+ }
+
+ /* 1. Write storage status */
+ fputs(buf, fptr);
+
+ /* 2. Write battery status */
+ if (batteryp >= 0)
+ fputs(batbuf, fptr);
+
+ /* 3. Write sluggishness type */
+ if (slug_vertical == GRAPHICS)
+ fputs("Sluggishness type: GRAPHICS\n", fptr);
+ else if (slug_vertical == MULTIMEDIA)
+ fputs("Sluggishness type: MULTIMEDIA\n", fptr);
+
+ /* 4. Write sluggish PID and process name */
+ fprintf(fptr,"Sluggish PID:%d\n", pid);
+
+ /* Get process name */
+ snprintf(proc, sizeof(proc), "/proc/%d/cmdline", pid);
+ fp = fopen(proc, "rb");
+ if (fp) {
+ memset(buf, 0x00, sizeof(buf));
+ size = fread(buf, 1, MAX_BUF_LEN, fp);
+ if (size > 0) {
+ fputs("Sluggish Process: ", fptr);
+ fputs(buf, fptr);
+ fputs("\n", fptr);
+ }
+ fclose(fp);
+ }
+
+ /* 5. Write CPU usage */
+ fprintf(fptr,"CPU used:%3.2lf%%, idle:%3.2lf%%\n", cpu_usage, (100 - cpu_usage));
+ fclose(fptr);
+ }
+
-static const struct module_ops sluggish_ops = {
++static void sluggish_get_sys_status(char *timestamp, long slug_vertical, int pid)
+ {
+ char dir_name[MAX_FILENAME_LEN];
+ double cu;
+ struct cpu_stat cs1;
+ struct cpu_stat cs2;
+
+ /* Get first cpu stat reading */
+ memset(&cs1, 0x00, sizeof(struct cpu_stat));
+ memset(&cs2, 0x00, sizeof(struct cpu_stat));
+
+ proc_cpu_stat(&cs1);
+
+ /* Get current timestamp */
+ time_stamp(timestamp);
+
+ /*
+ * Create dir to store the system snapshot
+ * All the data captured on sluggish detection notification will be
+ * stored in this directory for uploading later
+ */
+ /* Note : Data is captured only on receiving "SluggishDetected" signal */
+ snprintf(dir_name, sizeof(dir_name), "%s/%s", SLUGGISH_PATH, timestamp);
+ if (mkdir(dir_name, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
+ if (errno != EEXIST) {
+ _E("Failed to create dir %s", dir_name);
+ return;
+ }
+ }
+ _I("Created %s successfully", dir_name);
+
+ /* Get process Status */
+ sluggish_get_psinfo(timestamp);
+
+ /* Get Memory Status */
+ sluggish_get_mem_status(timestamp);
+
+ /* Get dlog */
+ sluggish_get_dlog(timestamp);
+
+ /* Get second cpu stat reading */
+ proc_cpu_stat(&cs2);
+
+ /* Get CPU usage % */
+ cu = proc_cpu_usage(&cs1, &cs2);
+
+ /* Get storage, battaery, cpu status */
+ sluggish_get_summary(timestamp, slug_vertical, pid, cu);
+
+ /* If current CPU utilization is > threshold (CPU_THRESHOLD) delay popup display */
+ if ((unsigned int)cu > CPU_THRESHOLD) {
+ _I("CPU used(%d%%) > %d%% - Delaying popup display ", (unsigned int)cu, CPU_THRESHOLD);
+ g_timeout_add_seconds(POPUP_DELAY_TIME, sluggish_popup_cb, (void *)slug_vertical);
+ return;
+ }
+ sluggish_launch_popup(slug_vertical);
+ }
+
+ static void sluggish_graphics(pid_t pid)
+ {
+ char timestamp[MAX_TIMESTAMP_LEN];
+ struct timeval timeval;
+ struct timeval interval;
+ static pid_t sluggish_graphics_last_pid; /* PID of last graphics sluggishness */
+ static struct timeval sluggish_graphics_last_ts; /* Timestamp of last graphics sluggishness */
+
+ /* Process graphics sluggishness */
+ _I("Graphics sluggishness for PID (%d), Count:%d", pid, ++sluggish_count);
+ if (gettimeofday(&timeval, NULL)) {
+ _E("gettimeofday() failed");
+ return;
+ }
+ if (pid == sluggish_graphics_last_pid) {
+ time_diff(&interval, &sluggish_graphics_last_ts, &timeval);
+ _D("Current ts-sec:%ld usec:%ld", timeval.tv_sec, timeval.tv_usec);
+ _D("Last ts-sec:%ld usec:%ld", sluggish_graphics_last_ts.tv_sec, sluggish_graphics_last_ts.tv_usec);
+ _D("Diff-%ld sec, %ld usec\n", interval.tv_sec, interval.tv_usec);
+ /*
+ *If Duplicate(Same PID, Same sluggish type)
+ * "SluggisgDetected" notification is received
+ * within SLUGGISH_DUP_NOTI_TIMELIMIT ignore it
+ */
+ if (interval.tv_sec <= SLUGGISH_DUP_NOTI_TIMELIMIT) {
+ _I("Ignoring update for same PID:%d within timelimit(%ds)", pid, SLUGGISH_DUP_NOTI_TIMELIMIT);
+ return;
+ }
+ }
+ sluggish_graphics_last_pid = pid;
+ sluggish_graphics_last_ts.tv_sec = timeval.tv_sec;
+ sluggish_graphics_last_ts.tv_usec = timeval.tv_usec;
+
+ /* Get system snapshot and dump to SLUGGISH_PATH */
+ sluggish_get_sys_status(timestamp, GRAPHICS, pid);
+ }
+
+ static void sluggish_multimedia(pid_t pid)
+ {
+ char timestamp[MAX_TIMESTAMP_LEN];
+ struct timeval timeval;
+ struct timeval interval;
+ static pid_t sluggish_media_last_pid; /* PID of last multimedia sluggishness */
+ static struct timeval sluggish_media_last_ts; /* Timestamp of last graphics sluggishness */
+
+ /* Process multimedia sluggishness */
+ _I("Multimedia sluggishness for PID (%d), Count:%d", pid, ++sluggish_count);
+ if (gettimeofday(&timeval, NULL)) {
+ _E("gettimeofday() failed");
+ return;
+ }
+ if (pid == sluggish_media_last_pid) {
+ time_diff(&interval, &sluggish_media_last_ts, &timeval);
+ _D("Current ts-sec:%ld usec:%ld", timeval.tv_sec, timeval.tv_usec);
+ _D("Last ts-sec:%ld usec:%ld", sluggish_media_last_ts.tv_sec, sluggish_media_last_ts.tv_usec);
+ _D("Diff-%ld sec, %ld usec\n", interval.tv_sec, interval.tv_usec);
+ /*
+ * If Duplicate(Same PID, Same sluggish type)
+ * "SluggisgDetected" notification is received
+ * within SLUGGISH_DUP_NOTI_TIMELIMIT ignore it
+ */
+ if (interval.tv_sec <= SLUGGISH_DUP_NOTI_TIMELIMIT) {
+ _I("Ignoring update for same PID:%d within timelimit(%ds)", pid, SLUGGISH_DUP_NOTI_TIMELIMIT);
+ return;
+ }
+ }
+ sluggish_media_last_pid = pid;
+ sluggish_media_last_ts.tv_sec = timeval.tv_sec;
+ sluggish_media_last_ts.tv_usec = timeval.tv_usec;
+
+ /* Get system snapshot and dump to SLUGGISH_PATH */
+ sluggish_get_sys_status(timestamp, MULTIMEDIA, pid);
+ }
+
+ static void sluggish_process(enum sluggish_event event, pid_t pid)
+ {
+ switch (event) {
+ case GRAPHICS:
+ sluggish_graphics(pid);
+ break;
+ case MULTIMEDIA:
+ sluggish_multimedia(pid);
+ break;
+ default:
+ break;
+ };
+ }
+
+ static DBusMessage *edbus_sluggish_detected(E_DBus_Object *obj, DBusMessage *msg)
+ {
+ DBusMessage *reply;
+ dbus_bool_t ret;
+ DBusError err;
+ pid_t pid = 0;
+ int slug_vertical = 0;
+
+ dbus_error_init(&err);
+
+ ret = dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &slug_vertical,
+ DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID);
+ if (ret == FALSE) {
+ _E("Can't deserialize sluggish detection message ![%s:%s]\n",
+ err.name, err.message);
+ goto error;
+ }
+
+ dbus_error_free(&err);
+ if (slug_vertical != GRAPHICS && slug_vertical != MULTIMEDIA) {
+ _E("Invalid sluggish vertical:%d", slug_vertical);
+ goto error;
+ }
+
+ /* Received "SluggishDetected" message, so get system details and dump to SLUGGISH_PATH */
+ sluggish_process(slug_vertical, pid);
+ error:
+ reply = dbus_message_new_method_return(msg);
+ return reply;
+ }
+
+ static const struct edbus_method edbus_methods[] = {
+ { "SluggishDetected", NULL, "ii", edbus_sluggish_detected },
+ /* Add methods here */
+ };
+
+ static int sluggish_init(void *data)
+ {
+ resourced_ret_c ret;
+ /* Create SLUGGISH_PATH folder to dump system snapshot*/
+ if (mkdir(SLUGGISH_PATH, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
+ if (errno != EEXIST) {
+ _E("Failed to create dir %s", SLUGGISH_PATH);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ _I("Created %s successfully", SLUGGISH_PATH);
+ ret = edbus_add_methods(RESOURCED_PATH_SLUGGISH, edbus_methods,
+ ARRAY_SIZE(edbus_methods));
+
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("DBus method registration for %s is failed", RESOURCED_PATH_SLUGGISH);
+ return ret;
+ }
+
+ return RESOURCED_ERROR_NONE;
+ }
+
++static struct module_ops sluggish_ops = {
+ .priority = MODULE_PRIORITY_NORMAL,
+ .name = "sluggish",
+ .init = sluggish_init,
+ };
+
+ MODULE_REGISTER(&sluggish_ops)
#define BtoMB(x) ((x) >> 20)
#define BtoPAGE(x) ((x) >> 12)
- #define SWAP_TIMER_INTERVAL 0.5
#define SWAP_PRIORITY 20
-
#define SWAP_SORT_MAX 10
- #define SWAP_COUNT_MAX 5
+ #define MAX_PIDS 3
+ #define SWAP_RATIO 0.5
+ #define SWAP_FULLNESS_RATIO 0.8
+ #define SWAP_FORCE_RECLAIM_NUM_MAX 5
+ #define SWAP_RECLIAM_PAGES_MAX 2560
+ #define SWAP_RECLIAM_PAGES_MIN 128
+
+ enum swap_thread_op {
+ SWAP_OP_ACTIVATE,
+ SWAP_OP_RECLAIM,
+ SWAP_OP_COMPACT,
+ SWAP_OP_END,
+ };
- struct task_info {
- pid_t pid;
- pid_t pgid;
- int oom_score_adj;
+ struct swap_task {
+ struct proc_app_info *pai;
int size;
- int cgroup_cnt;
};
- static int swapon;
- static float hard_limit_fraction = SWAP_HARD_LIMIT_DEFAULT;
+ struct swap_zram_control {
+ int max_comp_streams;
+ char comp_algorithm[5];
+ float ratio;
+ unsigned long swap_size_bytes;
+ unsigned long swap_almost_full_bytes;
+ };
+
+ struct swap_safe_queue {
+ GQueue *queue;
+ pthread_mutex_t lock;
+ };
+
+ struct swap_thread_bundle {
+ struct swap_status_msg msg;
+ enum swap_thread_op op;
+ };
+
+ static struct swap_zram_control swap_control = {
+ .max_comp_streams = -1,
+ .comp_algorithm = "lzo",
+ .ratio = SWAP_RATIO,
+ .swap_size_bytes = 0,
+ .swap_almost_full_bytes = 0,
+ };
+
static pthread_mutex_t swap_mutex;
static pthread_cond_t swap_cond;
- static Ecore_Timer *swap_timer = NULL;
+ static struct swap_safe_queue swap_thread_queue;
-static const struct module_ops swap_modules_ops;
++static struct module_ops swap_modules_ops;
++static char *swap_thread_op_names[SWAP_OP_END] = {
++ "ACTIVATE",
++ "RECLAIM",
++ "COMPACT",
++};
- static const struct module_ops swap_modules_ops;
- static const struct module_ops *swap_ops;
+ static int swap_compact_handler(void *data);
+
+ static const char *compact_reason_to_str(enum swap_compact_reason reason)
+ {
+ static const char *reasons_table[] = {"lowmem: low", "lowmem: medium",
+ "swap: zram full"};
+ if (reason >= SWAP_COMPACT_LOWMEM_LOW && reason < SWAP_COMPACT_RESASON_MAX)
+ return reasons_table[reason];
+ return "";
+ }
- static int swap_get_swap_type(void)
+ enum swap_state swap_get_state(void)
{
struct shared_modules_data *modules_data = get_shared_modules_data();
return pid;
}
- static Eina_Bool swap_send_signal(void *data)
+ static int swap_zram_activate(void)
{
int ret;
+ unsigned int swap_size_bytes;
- _D("swap timer callback function start");
+ ret = fwrite_int(SWAP_ZRAM_MAX_COMP_STREAMS, swap_control.max_comp_streams);
+ if (ret < 0) {
+ _E("fail to write max_comp_streams");
+ return ret;
+ }
- if(!swapon)
- swap_on();
+ ret = fwrite_str(SWAP_ZRAM_COMP_ALGORITHM, swap_control.comp_algorithm);
+ if (ret < 0) {
+ _E("fail to write comp_algrithm");
+ return ret;
+ }
- /* signal to swap_start to start swap */
- ret = pthread_mutex_trylock(&swap_mutex);
+ swap_control.swap_size_bytes = swap_size();
+ swap_control.swap_almost_full_bytes = swap_control.swap_size_bytes * SWAP_FULLNESS_RATIO;
+ ret = fwrite_uint(SWAP_ZRAM_DISK_SIZE, swap_control.swap_size_bytes);
+ if (ret < 0) {
+ _E("fail to write disk_size");
+ return ret;
+ }
- 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);
+ ret = fread_uint(SWAP_ZRAM_DISK_SIZE, &swap_size_bytes);
+ if (ret < 0) {
+ _E("fail to read zram disk_size");
+ return ret;
}
- _D("swap timer delete");
+ /* Check if zram was sucessfully initialized (zcomp rollback case) */
+ if (swap_size_bytes < swap_control.swap_size_bytes)
+ return RESOURCED_ERROR_OOM;
- ecore_timer_del(swap_timer);
- swap_timer = NULL;
+ ret = swap_mkswap();
+ if (ret < 0) {
+ _E("swap mkswap failed, fork error = %d", ret);
+ return RESOURCED_ERROR_FAIL;
+ }
- return ECORE_CALLBACK_CANCEL;
+ return RESOURCED_ERROR_NONE;
}
- static int swap_start(void *data)
+ static void swap_activate_in_module(void)
{
- if (swap_timer == NULL) {
- _D("swap timer start");
- swap_timer =
- ecore_timer_add(SWAP_TIMER_INTERVAL, swap_send_signal, (void *)NULL);
+ int disksize;
+
+ if (swap_get_state() == SWAP_ON)
+ return;
+
+ disksize = swap_get_disksize_bytes();
+ if (disksize <= 0) {
+ if (swap_zram_activate() < 0) {
+ _E("swap cannot be activated");
+ return;
+ }
}
+ swap_change_state(SWAP_ON);
+ _D("swap activated");
+ }
- return RESOURCED_ERROR_NONE;
+ static void *swap_thread_main(void * data)
+ {
+ int is_empty;
+ struct swap_thread_bundle *bundle;
+
+ setpriority(PRIO_PROCESS, 0, SWAP_PRIORITY);
+
+ while (1) {
+ pthread_mutex_lock(&swap_mutex);
+ /* THREAD: WAIT FOR START */
+ pthread_mutex_lock(&swap_thread_queue.lock);
+ is_empty = g_queue_is_empty(swap_thread_queue.queue);
+ pthread_mutex_unlock(&swap_thread_queue.lock);
+ if (is_empty) {
+ /* The queue is empty, wait for thread signal */
+ pthread_cond_wait(&swap_cond, &swap_mutex);
+ }
+
+ /* We're in swap thread, now it's time to dispatch bundles */
+ pthread_mutex_lock(&swap_thread_queue.lock);
+ bundle = g_queue_pop_head(swap_thread_queue.queue);
+ pthread_mutex_unlock(&swap_thread_queue.lock);
+
+ if (!bundle)
+ goto unlock_out;
+
+ switch (bundle->op) {
+ /* Swap activation operttion: mkswap, swapon etc. */
+ case SWAP_OP_ACTIVATE:
+ swap_activate_in_module();
+ break;
+ /* Swap reclaim opertation: move to swap, force_reclaim */
+ case SWAP_OP_RECLAIM:
+ swap_reclaim_memcg(bundle->msg);
+ break;
+ /* Swap compact operation of zsmalloc. */
+ case SWAP_OP_COMPACT:
+ swap_compact_zram();
+ break;
+ case SWAP_OP_END:
+ default:
+ _D("wrong swap thread operation selected");
+ }
+
+ free(bundle);
+ unlock_out:
+ pthread_mutex_unlock(&swap_mutex);
+ }
+ return NULL;
}
- static int swap_thread_create(void)
+ static int swap_start_handler(void *data)
{
- int ret = RESOURCED_ERROR_NONE;
- pthread_t pth;
+ int ret;
+ struct swap_thread_bundle *bundle;
- pthread_mutex_init(&swap_mutex, NULL);
- pthread_cond_init(&swap_cond, NULL);
+ if (!data)
+ return RESOURCED_ERROR_NO_DATA;
- ret = pthread_create(&pth, NULL, &swap_thread_main, (void*)NULL);
- if (ret) {
- _E("pthread creation for swap_thread failed\n");
- return ret;
+ bundle = malloc(sizeof(struct swap_thread_bundle));
+ if (!bundle)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ bundle->op = SWAP_OP_RECLAIM;
+ memcpy(&(bundle->msg), data, sizeof(struct swap_status_msg));
+
+ if (bundle->msg.type == MEMCG_BACKGROUND) {
+ ret = swap_move_background_to_swap(&(bundle->msg));
+ /* add bundle only if some processes were moved into swap memcg */
+ if (ret) {
+ free(bundle);
+ return RESOURCED_ERROR_NO_DATA;
+ }
+ }
+ swap_add_bundle(bundle);
+
+ /* Try to signal swap thread, that there is some work to do */
+ ret = pthread_mutex_trylock(&swap_mutex);
+ if (ret == 0) {
+ pthread_cond_signal(&swap_cond);
+ pthread_mutex_unlock(&swap_mutex);
+ _I("send signal to swap thread");
+ return RESOURCED_ERROR_NONE;
+ }
+
+ if (ret && ret == EBUSY) {
+ _D("swap thread already active");
} else {
- pthread_detach(pth);
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
}
- return ret;
+ return RESOURCED_ERROR_NONE;
}
- int swap_check_swap_pid(int pid)
+ static int swap_simple_bundle_sender(enum swap_thread_op operation)
{
- char buf[SWAP_PATH_MAX] = {0,};
- int swappid;
- int ret = 0;
- FILE *f;
+ int ret;
+ struct swap_thread_bundle *bundle;
- f = fopen(SWAPCG_PROCS, "r");
- if (!f) {
- _E("%s open failed", SWAPCG_PROCS);
- return RESOURCED_ERROR_FAIL;
+ bundle = malloc(sizeof(struct swap_thread_bundle));
+ if (!bundle)
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+
+ bundle->op = operation;
+ swap_add_bundle(bundle);
+
++ if (operation >= 0 && operation < SWAP_OP_END)
++ _D("added %s operation to swap queue",
++ swap_thread_op_names[operation]);
++
+ /* Try to signal swap thread, that there is some work to do */
+ ret = pthread_mutex_trylock(&swap_mutex);
+ if (ret == 0) {
+ pthread_cond_signal(&swap_cond);
+ pthread_mutex_unlock(&swap_mutex);
+ _I("send signal to swap thread");
+ return RESOURCED_ERROR_NONE;
}
- while (fgets(buf, SWAP_PATH_MAX, f) != NULL) {
- swappid = atoi(buf);
- if (swappid == pid) {
- ret = swappid;
- break;
- }
+ if (ret && ret == EBUSY) {
+ _D("swap thread already active");
+ } else {
+ _E("pthread_mutex_trylock fail : %d, errno : %d", ret, errno);
+ return RESOURCED_ERROR_FAIL;
}
- fclose(f);
- return ret;
+ return RESOURCED_ERROR_NONE;
+ }
+
+ static int swap_activate_handler(void *data)
+ {
+ return swap_simple_bundle_sender(SWAP_OP_ACTIVATE);
+ }
+
+ static int swap_compact_handler(void *data)
+ {
+ _I("compaction request. Reason: %s",
+ compact_reason_to_str((enum swap_compact_reason)data));
+ return swap_simple_bundle_sender(SWAP_OP_COMPACT);
}
static void swap_start_pid_edbus_signal_handler(void *data, DBusMessage *msg)
return RESOURCED_ERROR_NONE;
}
--static const struct module_ops swap_modules_ops = {
++static struct module_ops swap_modules_ops = {
.priority = MODULE_PRIORITY_NORMAL,
.name = "swap",
.init = resourced_swap_init,
return RESOURCED_ERROR_NONE;
}
--static const struct module_ops timer_modules_ops = {
++static struct module_ops timer_modules_ops = {
.priority = MODULE_PRIORITY_NORMAL,
.name = TIMER_MODULE_NAME,
.init = resourced_timer_slack_init,