From c18a94f4df348d78f3a972037cfcbb7358dc36a2 Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Mon, 15 Jun 2015 11:46:19 +0900 Subject: [PATCH 2/7] Migrate from 2.4 code repo Change-Id: I3186ef9a0ee6cf56696ec64e6962823023420fda Signed-off-by: Mu-Woong --- .gitignore | 1 + AUTHORS | 3 + CMakeLists.txt | 70 ++ LICENSE | 204 ++++++ data/access-config.xml | 54 ++ data/template-json-to-sql.sh | 34 + data/trigger-template.json | 105 +++ packaging/context-service.manifest | 68 ++ packaging/context-service.service | 15 + packaging/context-service.spec | 124 ++++ packaging/context-service.xml | 21 + packaging/org.tizen.context.service | 6 + src/access_control/config_loader.cpp | 100 +++ src/access_control/config_loader.h | 28 + src/access_control/privilege.cpp | 99 +++ src/access_control/privilege.h | 35 + src/client_request.cpp | 160 +++++ src/client_request.h | 47 ++ src/context_mgr_impl.cpp | 434 ++++++++++++ src/context_mgr_impl.h | 79 +++ src/context_trigger/clips_handler.cpp | 271 ++++++++ src/context_trigger/clips_handler.h | 56 ++ src/context_trigger/context_monitor.cpp | 233 +++++++ src/context_trigger/context_monitor.h | 58 ++ src/context_trigger/fact.cpp | 100 +++ src/context_trigger/fact.h | 56 ++ src/context_trigger/fact_reader.cpp | 245 +++++++ src/context_trigger/fact_reader.h | 77 +++ src/context_trigger/fact_request.cpp | 67 ++ src/context_trigger/fact_request.h | 43 ++ src/context_trigger/rule_manager.cpp | 1052 ++++++++++++++++++++++++++++++ src/context_trigger/rule_manager.h | 68 ++ src/context_trigger/script_generator.cpp | 516 +++++++++++++++ src/context_trigger/script_generator.h | 39 ++ src/context_trigger/timer.cpp | 285 ++++++++ src/context_trigger/timer.h | 74 +++ src/context_trigger/timer_types.h | 40 ++ src/context_trigger/trigger.cpp | 318 +++++++++ src/context_trigger/trigger.h | 72 ++ src/db_mgr_impl.cpp | 396 +++++++++++ src/db_mgr_impl.h | 88 +++ src/dbus_server_impl.cpp | 231 +++++++ src/dbus_server_impl.h | 44 ++ src/request.cpp | 67 ++ src/request.h | 54 ++ src/server.cpp | 198 ++++++ src/server.h | 33 + src/timer_mgr_impl.cpp | 223 +++++++ src/timer_mgr_impl.h | 43 ++ src/zone_util_impl.cpp | 108 +++ src/zone_util_impl.h | 43 ++ 51 files changed, 6885 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 data/access-config.xml create mode 100644 data/template-json-to-sql.sh create mode 100644 data/trigger-template.json create mode 100644 packaging/context-service.manifest create mode 100644 packaging/context-service.service create mode 100644 packaging/context-service.spec create mode 100644 packaging/context-service.xml create mode 100644 packaging/org.tizen.context.service create mode 100644 src/access_control/config_loader.cpp create mode 100644 src/access_control/config_loader.h create mode 100644 src/access_control/privilege.cpp create mode 100644 src/access_control/privilege.h create mode 100644 src/client_request.cpp create mode 100644 src/client_request.h create mode 100644 src/context_mgr_impl.cpp create mode 100644 src/context_mgr_impl.h create mode 100644 src/context_trigger/clips_handler.cpp create mode 100644 src/context_trigger/clips_handler.h create mode 100644 src/context_trigger/context_monitor.cpp create mode 100644 src/context_trigger/context_monitor.h create mode 100644 src/context_trigger/fact.cpp create mode 100644 src/context_trigger/fact.h create mode 100644 src/context_trigger/fact_reader.cpp create mode 100644 src/context_trigger/fact_reader.h create mode 100644 src/context_trigger/fact_request.cpp create mode 100644 src/context_trigger/fact_request.h create mode 100644 src/context_trigger/rule_manager.cpp create mode 100644 src/context_trigger/rule_manager.h create mode 100644 src/context_trigger/script_generator.cpp create mode 100644 src/context_trigger/script_generator.h create mode 100644 src/context_trigger/timer.cpp create mode 100644 src/context_trigger/timer.h create mode 100644 src/context_trigger/timer_types.h create mode 100644 src/context_trigger/trigger.cpp create mode 100644 src/context_trigger/trigger.h create mode 100644 src/db_mgr_impl.cpp create mode 100644 src/db_mgr_impl.h create mode 100644 src/dbus_server_impl.cpp create mode 100644 src/dbus_server_impl.h create mode 100644 src/request.cpp create mode 100644 src/request.h create mode 100644 src/server.cpp create mode 100644 src/server.h create mode 100644 src/timer_mgr_impl.cpp create mode 100644 src/timer_mgr_impl.h create mode 100644 src/zone_util_impl.cpp create mode 100644 src/zone_util_impl.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a01ee28 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.*.swp diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d97a819 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Mu-Woong Lee +Jihoon Park +Somin Kim diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4233766 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(context-service) +INCLUDE(GNUInstallDirs) + +# Targets +SET(target "contextd") + +# Source Lists +FILE(GLOB_RECURSE SRCS src/*.cpp) +MESSAGE("Sources: ${SRCS}") + +# Dependencies +SET(DEPS + vconf + libxml-2.0 + sqlite3 + capi-system-info + capi-appfw-app-manager + appsvc + pkgmgr-info + capi-security-privilege-manager + vasum + alarm-service + notification + clips + context-common + device-context-provider + statistics-context-provider + place-context-provider +) + +# Dependencies regarding profiles +IF("${PROFILE}" STREQUAL "mobile") + ADD_DEFINITIONS("-D_MOBILE") +ENDIF("${PROFILE}" STREQUAL "mobile") + +IF("${PROFILE}" STREQUAL "wearable") + ADD_DEFINITIONS("-D_WEARABLE") +ENDIF("${PROFILE}" STREQUAL "wearable") + +# Target vs Emulator +IF("${ARCH}" STREQUAL "arm") + ADD_DEFINITIONS("-D_TARGET") +ELSE("${ARCH}" STREQUAL "arm") + ADD_DEFINITIONS("-D_EMULATOR") +ENDIF("${ARCH}" STREQUAL "arm") + +# Common Options +INCLUDE(FindPkgConfig) +INCLUDE_DIRECTORIES( + /usr/include + /usr/include/glib-2.0 +) +ADD_DEFINITIONS(-g -O2 -Wall -fPIC -fvisibility=hidden -Wl,--as-needed) + +# Building Daemon +pkg_check_modules(daemon_pkg REQUIRED ${DEPS}) + +SET(DAEMON_EXTRA_CFLAGS -fPIE) +FOREACH(flag ${daemon_pkg_CFLAGS}) + SET(DAEMON_EXTRA_CFLAGS "${DAEMON_EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +ADD_EXECUTABLE(${target} ${SRCS}) +TARGET_LINK_LIBRARIES(${target} ${daemon_pkg_LDFLAGS} -pie) +SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_FLAGS ${DAEMON_EXTRA_CFLAGS}) +SET_TARGET_PROPERTIES(${target} PROPERTIES COMPILE_DEFINITIONS "LOG_TAG=\"CONTEXT\"") + +# Installing Daemon +INSTALL(TARGETS ${target} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1da314d --- /dev/null +++ b/LICENSE @@ -0,0 +1,204 @@ +Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/data/access-config.xml b/data/access-config.xml new file mode 100644 index 0000000..2c4f6c0 --- /dev/null +++ b/data/access-config.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/template-json-to-sql.sh b/data/template-json-to-sql.sh new file mode 100644 index 0000000..c5dba2c --- /dev/null +++ b/data/template-json-to-sql.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +function print_sql { + echo "UPDATE context_trigger_template SET j_template='{$2 }' WHERE name='$1';" + echo "INSERT OR IGNORE INTO context_trigger_template (name, j_template) VALUES ('$1', '{$2 }');" +} + +echo "CREATE TABLE IF NOT EXISTS context_trigger_template (" +echo " name TEXT DEFAULT '' NOT NULL PRIMARY KEY," +echo " j_template TEXT DEFAULT '' NOT NULL);" + +template_json_file="$1" + +while read -r line +do + keyword=`echo $line | awk '{print $1}' | tr -cd '[:alpha:]'` + + if [[ $keyword = "name" ]]; then + + if [ -n "$template" ]; then + print_sql "$name" "$template" + template="" + fi + + name=`echo $line | tr -d '":,' | awk '{print $2}'` + fi + + if [[ $keyword = "name" ]] || [[ $keyword = "attributes" ]] || [[ $keyword = "option" ]]; then + template="$template $line" + fi + +done < "$template_json_file" + +print_sql "$name" "$template" diff --git a/data/trigger-template.json b/data/trigger-template.json new file mode 100644 index 0000000..5a32ec9 --- /dev/null +++ b/data/trigger-template.json @@ -0,0 +1,105 @@ +{ + "templates": [ + { + "name": "timer/event", + "attributes": ["TimeOfDay", "DayOfWeek"] + }, + { + "name": "timer/state", + "attributes": ["TimeOfDay", "DayOfWeek", "DayOfMonth"] + }, + { + "name": "system/state/battery", + "attributes": ["Level", "IsCharging"] + }, + { + "name": "system/state/charger", + "attributes": ["IsConnected"] + }, + { + "name": "system/state/flight_mode", + "attributes": ["IsEnabled"] + }, + { + "name": "system/state/gps", + "attributes": ["State"] + }, + { + "name": "system/state/headphone", + "attributes": ["IsConnected", "Type"] + }, + { + "name": "system/state/ps_mode", + "attributes": ["IsEnabled"] + }, + { + "name": "system/state/silent_mode", + "attributes": ["IsEnabled"] + }, + { + "name": "system/state/vibration_mode", + "attributes": ["IsEnabled"] + }, + { + "name": "system/state/usb", + "attributes": ["IsConnected"] + }, + { + "name": "system/state/wifi", + "attributes": ["State", "BSSID"] + }, + { + "name": "social/state/call", + "attributes": ["Medium", "State", "Address"] + }, + { + "name": "social/event/email", + "attributes": ["Event"] + }, + { + "name": "social/event/message", + "attributes": ["Event", "Type", "Address"] + }, + { + "name": "activity/event/stationary", + "attributes": ["Event", "Accuracy"] + }, + { + "name": "activity/event/walking", + "attributes": ["Event", "Accuracy"] + }, + { + "name": "activity/event/running", + "attributes": ["Event", "Accuracy"] + }, + { + "name": "activity/event/in_vehicle", + "attributes": ["Event", "Accuracy"] + }, + { + "name": "place/event/geofence", + "attributes": ["Event"], + "option": ["PlaceId"] + }, + { + "name": "app/history/use_freq", + "attributes": ["Rank", "TotalCount"], + "option": ["AppId", "TimeOfDay", "DayOfWeek"] + }, + { + "name": "contact/history/comm_freq", + "attributes": ["Rank", "TotalCount"], + "option": ["Address", "TimeOfDay", "DayOfWeek"] + }, + { + "name": "music/history/play_freq", + "attributes": ["TotalCount"], + "option": ["TimeOfDay", "DayOfWeek"] + }, + { + "name": "video/history/play_freq", + "attributes": ["TotalCount"], + "option": ["TimeOfDay", "DayOfWeek"] + } + ] +} diff --git a/packaging/context-service.manifest b/packaging/context-service.manifest new file mode 100644 index 0000000..9ee0705 --- /dev/null +++ b/packaging/context-service.manifest @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/context-service.service b/packaging/context-service.service new file mode 100644 index 0000000..d8f8a21 --- /dev/null +++ b/packaging/context-service.service @@ -0,0 +1,15 @@ +[Unit] +Description=Contextual Service Framework Daemon + +[Service] +Type=dbus +User=system +Group=system +SmackProcessLabel=context-service +BusName=org.tizen.context +ExecStart=/usr/bin/contextd +Restart=always +RestartSec=1 + +[Install] +WantedBy=graphical.target diff --git a/packaging/context-service.spec b/packaging/context-service.spec new file mode 100644 index 0000000..879c694 --- /dev/null +++ b/packaging/context-service.spec @@ -0,0 +1,124 @@ +Name: context-service +Summary: Context-Service +Version: 0.4.4 +Release: 1 +Group: Framework/system +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1: context-service.service +Source2: org.tizen.context.service + +BuildRequires: cmake +BuildRequires: sed +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(libxml-2.0) +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(capi-system-info) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: pkgconfig(appsvc) +BuildRequires: pkgconfig(pkgmgr-info) +BuildRequires: pkgconfig(capi-security-privilege-manager) +BuildRequires: pkgconfig(vasum) +BuildRequires: pkgconfig(alarm-service) +BuildRequires: pkgconfig(notification) + +BuildRequires: pkgconfig(clips) +BuildRequires: pkgconfig(context-common) +BuildRequires: pkgconfig(context) +BuildRequires: context-internal + +BuildRequires: pkgconfig(device-context-provider) +BuildRequires: pkgconfig(statistics-context-provider) +BuildRequires: pkgconfig(place-context-provider) + +Requires(preun): /usr/bin/systemctl +Requires(post): sys-assert +Requires(post): /usr/bin/systemctl +Requires(post): /usr/bin/sqlite3 +Requires(postun): /usr/bin/systemctl + +%ifarch %{arm} +%define ARCH arm +%else +%define ARCH i586 +%endif + +%description +Context-Service + +%prep +%setup -q + +%build +MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` + +export CFLAGS+=" -Wextra -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wswitch-default" +export CXXFLAGS+=" -Wextra -Wcast-align -Wcast-qual -Wshadow -Wwrite-strings -Wswitch-default -Wnon-virtual-dtor -Wno-c++0x-compat" + +export CFLAGS+=" -Wno-unused-parameter -Wno-empty-body" +export CXXFLAGS+=" -Wno-unused-parameter -Wno-empty-body" + +export CFLAGS+=" -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-strict-aliasing -fno-unroll-loops -fsigned-char -fstrict-overflow -fno-common" +export CXXFLAGS+=" -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-strict-aliasing -fno-unroll-loops -fsigned-char -fstrict-overflow" +#export CXXFLAGS+=" -std=c++0x" + +export CFLAGS+=" -DTIZEN_ENGINEER_MODE" +export CXXFLAGS+=" -DTIZEN_ENGINEER_MODE" +export FFLAGS+=" -DTIZEN_ENGINEER_MODE" + +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DARCH=%{ARCH} -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DPROFILE=%{?tizen_profile_name} +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +mkdir -p %{buildroot}%{_unitdir} +#mkdir -p %{buildroot}%{_datadir}/dbus-1/services +mkdir -p %{buildroot}%{_datadir}/license +mkdir -p %{buildroot}%{_datadir}/packages +mkdir -p %{buildroot}/opt/dbspace +mkdir -p %{buildroot}/opt/data/context-service +sqlite3 %{buildroot}/opt/dbspace/.context-service.db "PRAGMA journal_mode = PERSIST;" +sqlite3 %{buildroot}/opt/dbspace/.context-service.db "CREATE TABLE VERSION (VERSION TEXT);" +sqlite3 %{buildroot}/opt/dbspace/.context-service.db "INSERT INTO VERSION VALUES ('%{version}');" +install -m 0644 %{SOURCE1} %{buildroot}%{_unitdir} +#install -m 0644 %{SOURCE2} %{buildroot}%{_datadir}/dbus-1/services/ +cp LICENSE %{buildroot}%{_datadir}/license/%{name} +sed -i "s/^\tversion=\".*\"/\tversion=\"%{version}\"/g" packaging/context-service.xml +cp packaging/context-service.xml %{buildroot}%{_datadir}/packages/ +cp data/access-config.xml %{buildroot}/opt/data/context-service/ +cp data/trigger-template.json %{buildroot}/opt/data/context-service/ +sh data/template-json-to-sql.sh data/trigger-template.json > %{buildroot}/opt/data/context-service/trigger-template.sql + +%post +sqlite3 -echo /opt/dbspace/.context-service.db < /opt/data/context-service/trigger-template.sql +mkdir -p %{_unitdir}/graphical.target.wants +ln -s ../context-service.service %{_unitdir}/graphical.target.wants/ +/sbin/ldconfig +systemctl daemon-reload +#if [ $1 == 1 ]; then +# systemctl restart context-service +#fi + +%preun +if [ $1 == 0 ]; then + systemctl stop context-service +fi + +%postun +rm -f %{_unitdir}/graphical.target.wants/context-service.service +systemctl daemon-reload +/sbin/ldconfig + +%files +%manifest packaging/%{name}.manifest +%defattr(-,root,root,-) +%{_bindir}/* +%{_unitdir}/context-service.service +#%{_datadir}/dbus-1/services/org.tizen.context.service +%{_datadir}/license/%{name} +%{_datadir}/packages/*.xml +%defattr(0600,system,system,-) +/opt/data/context-service/* +%config(noreplace) /opt/dbspace/.context-service.db* diff --git a/packaging/context-service.xml b/packaging/context-service.xml new file mode 100644 index 0000000..9ed1f7b --- /dev/null +++ b/packaging/context-service.xml @@ -0,0 +1,21 @@ + + + + Context Service + + + + + http://tizen.org/privilege/location + + diff --git a/packaging/org.tizen.context.service b/packaging/org.tizen.context.service new file mode 100644 index 0000000..702a2df --- /dev/null +++ b/packaging/org.tizen.context.service @@ -0,0 +1,6 @@ +[D-BUS Service] +Name=org.tizen.context +Exec=/bin/false +SystemdService=context-service.service +User=system +Group=system diff --git a/src/access_control/config_loader.cpp b/src/access_control/config_loader.cpp new file mode 100644 index 0000000..7b56f45 --- /dev/null +++ b/src/access_control/config_loader.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "privilege.h" +#include "config_loader.h" + +#define CONFIG_PATH "/opt/data/context-service/access-config.xml" +#define ELM_ROOT "ContextAccessConfig" +#define ELM_PRIV "Privilege" +#define ELM_LINK "Link" +#define ELM_ALLOW "Allow" +#define ATTR_NAME "name" +#define ATTR_SUBJ "subject" +#define IS_NODE_OF(node, element) (xmlStrcmp((node)->name, (const xmlChar *)(element)) == 0) + +static void parse_priv_node(xmlNodePtr cursor) +{ + char *prop = (char*)(xmlGetProp(cursor, (const xmlChar*)ATTR_NAME)); + IF_FAIL_VOID_TAG(prop, _E, "Failed to get '%s'", ATTR_NAME); + + std::string name = prop; + free(prop); + + cursor = cursor->xmlChildrenNode; + IF_FAIL_VOID_TAG(cursor, _E, "No child node exists"); + + while (cursor) { + if (!IS_NODE_OF(cursor, ELM_ALLOW)) { + _D("Skipping a node '%s'", (const char*)cursor->name); + cursor = cursor->next; + continue; + } + + prop = (char*)(xmlGetProp(cursor, (const xmlChar*)ATTR_SUBJ)); + if (prop == NULL) { + _E("Failed to get '%s'", ATTR_SUBJ); + cursor = cursor->next; + continue; + } + + _SI("Set Privilege: %s <- %s", prop, name.c_str()); + ctx::privilege_manager::set(prop, name.c_str()); + + free(prop); + cursor = cursor->next; + } +} + +static void parse_link_node(xmlNodePtr cursor) +{ + //TODO: Not supported yet + _D("Skipping a link node\n"); +} + +bool ctx::access_config_loader::load() +{ + xmlDocPtr doc = xmlParseFile(CONFIG_PATH); + IF_FAIL_RETURN_TAG(doc, false, _E, "XML parsing failed"); + + xmlNodePtr cursor = xmlDocGetRootElement(doc); + IF_FAIL_CATCH_TAG(cursor, _E, "No root node"); + IF_FAIL_CATCH_TAG(IS_NODE_OF(cursor, ELM_ROOT), _E, "Invalid root node"); + + cursor = cursor->xmlChildrenNode; + + while (cursor) { + if (IS_NODE_OF(cursor, ELM_PRIV)) { + parse_priv_node(cursor); + } else if (IS_NODE_OF(cursor, ELM_LINK)) { + parse_link_node(cursor); + } + + cursor = cursor->next; + } + + xmlFreeDoc(doc); + return true; + +CATCH: + xmlFreeDoc(doc); + return false; +} diff --git a/src/access_control/config_loader.h b/src/access_control/config_loader.h new file mode 100644 index 0000000..aa98018 --- /dev/null +++ b/src/access_control/config_loader.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_ACCESS_CONFIG_LOADER_H__ +#define __CONTEXT_ACCESS_CONFIG_LOADER_H__ + +namespace ctx { + namespace access_config_loader { + + bool load(); + + } +} /* namespace ctx */ + +#endif /* End of __CONTEXT_ACCESS_CONFIG_LOADER_H__ */ diff --git a/src/access_control/privilege.cpp b/src/access_control/privilege.cpp new file mode 100644 index 0000000..4af7fe4 --- /dev/null +++ b/src/access_control/privilege.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include "config_loader.h" +#include "privilege.h" + +typedef std::map string_map_t; + +static string_map_t *privilege_map = NULL; + +bool ctx::privilege_manager::init() +{ + IF_FAIL_RETURN(privilege_map == NULL, true); + + privilege_map = new(std::nothrow) string_map_t; + + if (!ctx::access_config_loader::load()) { + _E("Loading failed"); + delete privilege_map; + return false; + } + + return true; +} + +void ctx::privilege_manager::release() +{ + delete privilege_map; + privilege_map = NULL; +} + +void ctx::privilege_manager::set(const char* subject, const char* priv) +{ + (*privilege_map)[subject] = priv; +} + +bool ctx::privilege_manager::is_allowed(const char* pkg_id, const char* subject) +{ + IF_FAIL_RETURN_TAG(pkg_id && subject, true, _E, "Invalid parameter"); + IF_FAIL_RETURN_TAG(pkg_id[0]!='\0' && subject[0]!='\0', true, _E, "Invalid parameter"); + + string_map_t::iterator it = privilege_map->find(subject); + if (it == privilege_map->end()) { + // Non-Privileged Subject + return true; + } + + _D("PkgId: %s, Priv: %s", pkg_id, (it->second).c_str()); + std::string priv = "http://tizen.org/privilege/"; + priv += (it->second).c_str(); + int ret = privilege_checker_check_package_privilege(pkg_id, priv.c_str()); + _D("Privilege Check Result: %#x", ret); + return (ret == PRIV_CHECKER_ERR_NONE); +} + +std::string ctx::privilege_manager::get_pkg_id(const char* app_id) +{ + std::string pkg_id; + IF_FAIL_RETURN_TAG(app_id, pkg_id, _E, "Null AppId"); + + int ret; + pkgmgrinfo_appinfo_h app_info; + + ret = pkgmgrinfo_appinfo_get_appinfo(app_id, &app_info); + IF_FAIL_RETURN_TAG(ret == PMINFO_R_OK, pkg_id, _E, "Failed to get app_info"); + + char *pkg_name = NULL; + ret = pkgmgrinfo_appinfo_get_pkgname(app_info, &pkg_name); + if (ret != PMINFO_R_OK || pkg_name == NULL) { + pkgmgrinfo_appinfo_destroy_appinfo(app_info); + _E("Failed to get package name"); + return pkg_id; + } + + pkg_id = pkg_name; + pkgmgrinfo_appinfo_destroy_appinfo(app_info); + return pkg_id; +} diff --git a/src/access_control/privilege.h b/src/access_control/privilege.h new file mode 100644 index 0000000..3744945 --- /dev/null +++ b/src/access_control/privilege.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_PRIVILEGE_MANAGER_H__ +#define __CONTEXT_PRIVILEGE_MANAGER_H__ + +#include + +namespace ctx { + namespace privilege_manager { + + bool init(); + void release(); + + void set(const char* subject, const char* priv); + bool is_allowed(const char* pkg_id, const char* subject); + std::string get_pkg_id(const char* app_id); + + } /* namespace ctx::privilege_manager */ +} /* namespace ctx */ + +#endif /* End of __CONTEXT_PRIVILEGE_MANAGER_H__ */ diff --git a/src/client_request.cpp b/src/client_request.cpp new file mode 100644 index 0000000..d1b8a95 --- /dev/null +++ b/src/client_request.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "zone_util_impl.h" +#include "dbus_server_impl.h" +#include "access_control/privilege.h" +#include "client_request.h" + +ctx::client_request::client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv) + : request_info(type, client, req_id, subj, desc) + , invocation(inv) +{ + gsize size; + int client_pid; + char *decoded = NULL; + const char *zone_name = NULL; + char *pkg_id = NULL; + + decoded = reinterpret_cast(g_base64_decode(cookie, &size)); + IF_FAIL_CATCH_TAG(decoded, _E, "Cookie decoding failed"); + + raw_cookie = decoded; + client_pid = security_server_get_cookie_pid(decoded); + pkg_id = security_server_get_smacklabel_cookie(decoded); + g_free(decoded); + IF_FAIL_CATCH_TAG(client_pid > 0, _E, "Invalid PID (%d)", client_pid); + + if (pkg_id == NULL) { + _W(RED("security_server_get_smacklabel_cookie() failed")); + char* app_id = NULL; + app_manager_get_app_id(client_pid, &app_id); + client_app_id = ctx::privilege_manager::get_pkg_id(app_id); + g_free(app_id); + } else { + //FIXME: Yes.. this is actually the package id + client_app_id = pkg_id; + g_free(pkg_id); + } + + zone_name = ctx::zone_util::get_name_by_pid(client_pid); + IF_FAIL_CATCH_TAG(zone_name, _E, RED("Zone name retrieval failed")); + _zone_name = zone_name; + + _SD(CYAN("Package: '%s' / Zone: '%s'"), client_app_id.c_str(), zone_name); + return; + +CATCH: + invocation = NULL; + throw ERR_OPERATION_FAILED; +} + +ctx::client_request::~client_request() +{ + if (invocation) + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); +} + +const char* ctx::client_request::get_cookie() +{ + return raw_cookie.c_str(); +} + +const char* ctx::client_request::get_app_id() +{ + if (!client_app_id.empty()) + return client_app_id.c_str(); + + return NULL; +} + +bool ctx::client_request::reply(int error) +{ + IF_FAIL_RETURN(invocation, true); + + _I("Reply %#x", error); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + invocation = NULL; + return true; +} + +bool ctx::client_request::reply(int error, ctx::json& request_result) +{ + IF_FAIL_RETURN(invocation, true); + IF_FAIL_RETURN(_type != REQ_READ_SYNC, true); + + char *result = request_result.dup_cstr(); + IF_FAIL_RETURN_TAG(result, false, _E, "Memory allocation failed"); + + _I("Reply %#x", error); + _SD("Result: %s", result); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, EMPTY_JSON_OBJECT)); + invocation = NULL; + + g_free(result); + return true; +} + +bool ctx::client_request::reply(int error, ctx::json& request_result, ctx::json& data_read) +{ + if (invocation == NULL) { + return publish(error, data_read); + } + + char *result = NULL; + char *data = NULL; + + result = request_result.dup_cstr(); + IF_FAIL_CATCH_TAG(result, _E, "Memory allocation failed"); + + data = data_read.dup_cstr(); + IF_FAIL_CATCH_TAG(data, _E, "Memory allocation failed"); + + _I("Reply %#x", error); + _SD("Result: %s", result); + _SD("Data: %s", data); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", error, result, data)); + invocation = NULL; + + g_free(result); + g_free(data); + return true; + +CATCH: + g_free(result); + g_free(data); + return false; +} + +bool ctx::client_request::publish(int error, ctx::json& data) +{ + char *data_str = data.dup_cstr(); + IF_FAIL_RETURN_TAG(data_str, false, _E, "Memory allocation failed"); + + dbus_server::publish(_client.c_str(), _req_id, _subject.c_str(), error, data_str); + g_free(data_str); + + return true; +} diff --git a/src/client_request.h b/src/client_request.h new file mode 100644 index 0000000..fc4737e --- /dev/null +++ b/src/client_request.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_CLIENT_REQUEST_H__ +#define __CONTEXT_CLIENT_REQUEST_H__ + +#include +#include "request.h" + +namespace ctx { + + class client_request : public request_info { + public: + client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv); + ~client_request(); + + const char* get_cookie(); + const char* get_app_id(); + + bool reply(int error); + bool reply(int error, ctx::json& request_result); + bool reply(int error, ctx::json& request_result, ctx::json& data_read); + bool publish(int error, ctx::json& data); + + private: + GDBusMethodInvocation *invocation; + std::string raw_cookie; + std::string client_app_id; + std::string exec_path; + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_CLIENT_REQUEST_H__ */ diff --git a/src/context_mgr_impl.cpp b/src/context_mgr_impl.cpp new file mode 100644 index 0000000..159cdc5 --- /dev/null +++ b/src/context_mgr_impl.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include "server.h" +#include "context_mgr_impl.h" +#include "zone_util_impl.h" +#include "access_control/privilege.h" + +/* Analyzer Headers */ +#include +#include +#include +#include +#include + +ctx::context_manager_impl::context_manager_impl() +{ +} + +ctx::context_manager_impl::~context_manager_impl() +{ + release(); +} + +bool ctx::context_manager_impl::init() +{ + IF_FAIL_RETURN_TAG(provider_list.size()==0, false, _W, "Re-initialization"); + + try { + /* List of all providers */ + load_provider(new ctx::device_context_provider()); + load_provider(new ctx::app_statistics_provider()); + load_provider(new ctx::social_statistics_provider()); + load_provider(new ctx::media_statistics_provider()); + load_provider(new ctx::place_context_provider()); + + } catch (std::bad_alloc& ba) { + _E("Analyzer loading failed (bad alloc)"); + return false; + } catch (int e) { + _E("Analyzer loading failed (%#x)", e); + return false; + } + + return true; +} + +void ctx::context_manager_impl::release() +{ + subject_provider_map.clear(); + + for (provider_list_t::iterator it = provider_list.begin(); it != provider_list.end(); ++it) { + delete *it; + } + provider_list.clear(); + + for (request_list_t::iterator it = subscribe_request_list.begin(); it != subscribe_request_list.end(); ++it) { + delete *it; + } + subscribe_request_list.clear(); + + for (request_list_t::iterator it = read_request_list.begin(); it != read_request_list.end(); ++it) { + delete *it; + } + read_request_list.clear(); +} + +void ctx::context_manager_impl::load_provider(context_provider_iface* provider) +{ + if (!provider) { + _E("Analyzer NULL"); + throw static_cast(ERR_INVALID_PARAMETER); + } + + provider_list.push_back(provider); + + if (!provider->init()) { + _E("Analyzer initialization failed"); + throw ERR_OPERATION_FAILED; + } +} + +bool ctx::context_manager_impl::register_provider(const char* subject, ctx::context_provider_iface* cp) +{ + IF_FAIL_RETURN_TAG(subject && cp, false, _E, "Invalid parameter"); + + if (subject_provider_map.find(subject) != subject_provider_map.end()) { + _E("The provider for the subject '%s' is already registered.", subject); + return false; + } + + _I("Registering provider for '%s'", subject); + subject_provider_map[subject] = cp; + + return true; +} + +void ctx::context_manager_impl::assign_request(ctx::request_info* request) +{ + int req_type = request->get_type(); + context_provider_iface *provider = NULL; + + if (req_type != REQ_UNSUBSCRIBE) { + subject_provider_map_t::iterator it = subject_provider_map.find(request->get_subject()); + if (it == subject_provider_map.end()) { + _E("Unknown subject '%s'", request->get_subject()); + request->reply(ERR_NOT_SUPPORTED); + delete request; + return; + } + provider = it->second; + } + + std::string app_id; + // If the ClientAppId attribute exists but is empty. + if (request->get_description().get(NULL, COMMON_ATTR_CLIENT_APP_ID, &app_id)) { + if (app_id.empty() && request->get_app_id()) { + request->get_description().set(NULL, COMMON_ATTR_CLIENT_APP_ID, request->get_app_id()); + } + } + + switch (req_type) { + case REQ_SUBSCRIBE: + if (!check_permission(request)) { + request->reply(ERR_PERMISSION_DENIED); + delete request; + break; + } + subscribe(request, provider); + break; + case REQ_UNSUBSCRIBE: + unsubscribe(request); + break; + case REQ_READ: + case REQ_READ_SYNC: + if (!check_permission(request)) { + request->reply(ERR_PERMISSION_DENIED); + delete request; + break; + } + read(request, provider); + break; + case REQ_WRITE: + if (!check_permission(request)) { + request->reply(ERR_PERMISSION_DENIED); + delete request; + break; + } + write(request, provider); + break; + case REQ_SUPPORT: + if (provider->is_supported(request->get_subject(), request->get_zone_name())) { + request->reply(ERR_NONE); + } else { + request->reply(ERR_NOT_SUPPORTED); + } + delete request; + break; + default: + _E("Invalid type of request"); + delete request; + } +} + +bool ctx::context_manager_impl::is_supported(const char* subject, const char* zone) +{ + subject_provider_map_t::iterator it = subject_provider_map.find(subject); + IF_FAIL_RETURN(it != subject_provider_map.end(), false); + + return it->second->is_supported(subject, zone); +} + +ctx::context_manager_impl::request_list_t::iterator +ctx::context_manager_impl::find_request(request_list_t& r_list, std::string subject, json& option, const char* zone) +{ + return find_request(r_list.begin(), r_list.end(), subject, option, zone); +} + +ctx::context_manager_impl::request_list_t::iterator +ctx::context_manager_impl::find_request(request_list_t& r_list, std::string client, int req_id) +{ + request_list_t::iterator it; + for (it = r_list.begin(); it != r_list.end(); ++it) { + if (client == (*it)->get_client() && req_id == (*it)->get_id()) { + break; + } + } + return it; +} + +ctx::context_manager_impl::request_list_t::iterator +ctx::context_manager_impl::find_request(request_list_t::iterator begin, request_list_t::iterator end, std::string subject, json& option, const char* zone) +{ + //TODO: Do we need to consider the case that the inparam option is a subset of the request description? + request_list_t::iterator it; + for (it = begin; it != end; ++it) { + if (subject == (*it)->get_subject() && option == (*it)->get_description() && STR_EQ(zone, (*it)->get_zone_name())) { + break; + } + } + return it; +} + +bool ctx::context_manager_impl::check_permission(ctx::request_info* request) +{ + const char* app_id = request->get_app_id(); + _D("Peer AppID: %s", app_id); + IF_FAIL_RETURN_TAG(app_id, false, _E, "AppID NULL"); + IF_FAIL_RETURN_TAG(!STR_EQ(app_id, TRIGGER_CLIENT_NAME), true, _D, "Skipping permission check for Trigger"); + + scope_zone_joiner sz(request->get_zone_name()); + + bool allowed = ctx::privilege_manager::is_allowed(app_id, request->get_subject()); + IF_FAIL_RETURN_TAG(allowed, false, _W, "Permission denied"); + + return true; +} + +void ctx::context_manager_impl::subscribe(ctx::request_info* request, ctx::context_provider_iface* provider) +{ + _I(CYAN("[%s] '%s' subscribes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id()); + + ctx::json request_result; + int error = provider->subscribe(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name()); + + _D("Analyzer returned %d", error); + + if (!request->reply(error, request_result) || error != ERR_NONE) { + delete request; + return; + } + + subscribe_request_list.push_back(request); +} + +void ctx::context_manager_impl::unsubscribe(ctx::request_info* request) +{ + _I(CYAN("[%s] '%s' unsubscribes RID-%d"), request->get_zone_name(), request->get_client(), request->get_id()); + + // Search the subscribe request to be removed + request_list_t::iterator target = find_request(subscribe_request_list, request->get_client(), request->get_id()); + if (target == subscribe_request_list.end()) { + _W("Unknown request"); + delete request; + return; + } + + // Keep the pointer to the request found + request_info *req_found = *target; + + // Remove the request from the list + subscribe_request_list.erase(target); + + // Check if there exist the same requests + if (find_request(subscribe_request_list, req_found->get_subject(), req_found->get_description(), req_found->get_zone_name()) != subscribe_request_list.end()) { + // Do not stop detecting the subject + _D("A same request from '%s' exists", req_found->get_client()); + request->reply(ERR_NONE); + delete request; + delete req_found; + return; + } + + // Find the proper provider + subject_provider_map_t::iterator ca = subject_provider_map.find(req_found->get_subject()); + if (ca == subject_provider_map.end()) { + _E("Invalid subject '%s'", req_found->get_subject()); + delete request; + delete req_found; + return; + } + + // Stop detecting the subject + int error = ca->second->unsubscribe(req_found->get_subject(), req_found->get_description(), req_found->get_zone_name()); + request->reply(error); + delete request; + delete req_found; +} + +void ctx::context_manager_impl::read(ctx::request_info* request, ctx::context_provider_iface* provider) +{ + _I(CYAN("[%s] '%s' reads '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id()); + + ctx::json request_result; + int error = provider->read(request->get_subject(), request->get_description().str(), &request_result, request->get_zone_name()); + + _D("Analyzer returned %d", error); + + if (!request->reply(error, request_result) || error != ERR_NONE) { + delete request; + return; + } + + read_request_list.push_back(request); +} + +void ctx::context_manager_impl::write(ctx::request_info* request, ctx::context_provider_iface* provider) +{ + _I(CYAN("[%s] '%s' writes '%s' (RID-%d)"), request->get_zone_name(), request->get_client(), request->get_subject(), request->get_id()); + + ctx::json request_result; + int error = provider->write(request->get_subject(), request->get_description(), &request_result, request->get_zone_name()); + + _D("Analyzer returned %d", error); + + request->reply(error, request_result); + delete request; +} + +bool ctx::context_manager_impl::_publish(const char* subject, ctx::json option, int error, ctx::json data_updated, const char* zone) +{ + IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter"); + + _I("Publishing '%s'", subject); + _J("Option", option); + + request_list_t::iterator end = subscribe_request_list.end(); + request_list_t::iterator target = find_request(subscribe_request_list.begin(), end, subject, option, zone); + + while (target != end) { + if (!(*target)->publish(error, data_updated)) { + return false; + } + target = find_request(++target, end, subject, option, zone); + } + + return true; +} + +bool ctx::context_manager_impl::_reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read, const char* zone) +{ + IF_FAIL_RETURN_TAG(subject, false, _E, "Invalid parameter"); + + _I("Sending data of '%s'", subject); + _J("Option", option); + _J("Data", data_read); + _SI("Zone: '%s'", zone); + + request_list_t::iterator end = read_request_list.end(); + request_list_t::iterator target = find_request(read_request_list.begin(), end, subject, option, zone); + request_list_t::iterator prev; + + ctx::json dummy; + + while (target != end) { + (*target)->reply(error, dummy, data_read); + prev = target; + target = find_request(++target, end, subject, option, zone); + + delete *prev; + read_request_list.erase(prev); + } + + return true; +} + +struct published_data_s { + int type; + ctx::context_manager_impl *mgr; + std::string subject; + std::string zone; + int error; + ctx::json option; + ctx::json data; + published_data_s(int t, ctx::context_manager_impl *m, const char* s, ctx::json& o, int e, ctx::json& d, const char* z) + : type(t), mgr(m), subject(s), error(e) + { + option = o.str(); + data = d.str(); + zone = z; + } +}; + +gboolean ctx::context_manager_impl::thread_switcher(gpointer data) +{ + published_data_s *tuple = static_cast(data); + + switch (tuple->type) { + case REQ_SUBSCRIBE: + tuple->mgr->_publish(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str()); + break; + case REQ_READ: + tuple->mgr->_reply_to_read(tuple->subject.c_str(), tuple->option, tuple->error, tuple->data, tuple->zone.c_str()); + break; + default: + _W("Invalid type"); + } + + delete tuple; + return FALSE; +} + +bool ctx::context_manager_impl::publish(const char* subject, ctx::json& option, int error, ctx::json& data_updated, const char* zone) +{ + IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter"); + + published_data_s *tuple = new(std::nothrow) published_data_s(REQ_SUBSCRIBE, this, subject, option, error, data_updated, zone); + IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed"); + + g_idle_add(thread_switcher, tuple); + + return true; +} + +bool ctx::context_manager_impl::reply_to_read(const char* subject, ctx::json& option, int error, ctx::json& data_read, const char* zone) +{ + IF_FAIL_RETURN_TAG(subject && zone, false, _E, "Invalid parameter"); + + published_data_s *tuple = new(std::nothrow) published_data_s(REQ_READ, this, subject, option, error, data_read, zone); + IF_FAIL_RETURN_TAG(tuple, false, _E, "Memory allocation failed"); + + g_idle_add(thread_switcher, tuple); + + return true; +} diff --git a/src/context_mgr_impl.h b/src/context_mgr_impl.h new file mode 100644 index 0000000..9598d04 --- /dev/null +++ b/src/context_mgr_impl.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_MANAGER_IMPL_H__ +#define __CONTEXT_MANAGER_IMPL_H__ + +#include +#include +#include +#include +#include "request.h" + +#define TRIGGER_CLIENT_NAME "TRIGGER" + +namespace ctx { + + class context_manager_impl : public context_manager_iface { + public: + typedef std::list request_list_t; + + context_manager_impl(); + ~context_manager_impl(); + + bool init(); + void release(); + + void assign_request(ctx::request_info* request); + bool is_supported(const char* subject, const char* zone); + + // From the interface class + bool register_provider(const char* subject, ctx::context_provider_iface* cp); + bool publish(const char* subject, ctx::json& option, int error, ctx::json& data_updated, const char* zone); + bool reply_to_read(const char* subject, ctx::json& option, int error, ctx::json& data_read, const char* zone); + // --- + + private: + typedef std::vector provider_list_t; + typedef std::map subject_provider_map_t; + + provider_list_t provider_list; + request_list_t subscribe_request_list; + request_list_t read_request_list; + subject_provider_map_t subject_provider_map; + + void load_provider(ctx::context_provider_iface* provider); + + void subscribe(request_info* request, context_provider_iface* provider); + void unsubscribe(request_info* request); + void read(request_info* request, context_provider_iface* provider); + void write(request_info* request, context_provider_iface* provider); + + bool check_permission(request_info* request); + + static gboolean thread_switcher(gpointer data); + bool _publish(const char* subject, ctx::json option, int error, ctx::json data_updated, const char* zone); + bool _reply_to_read(const char* subject, ctx::json option, int error, ctx::json data_read, const char* zone); + + request_list_t::iterator find_request(request_list_t& r_list, std::string subject, json& option, const char* zone); + request_list_t::iterator find_request(request_list_t& r_list, std::string client, int req_id); + request_list_t::iterator find_request(request_list_t::iterator begin, request_list_t::iterator end, std::string subject, json& option, const char* zone); + + }; /* class context_manager_impl */ + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_MANAGER_IMPL_H__ */ diff --git a/src/context_trigger/clips_handler.cpp b/src/context_trigger/clips_handler.cpp new file mode 100644 index 0000000..4ac8378 --- /dev/null +++ b/src/context_trigger/clips_handler.cpp @@ -0,0 +1,271 @@ +/* + * context-service + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include "clips_handler.h" +#include "rule_manager.h" + +extern "C" +{ +#include +} + +static void* env = NULL; +static ctx::rule_manager* rule_mgr = NULL; +static int string_to_int(std::string str); + +ctx::clips_handler::clips_handler() +{ +} + +ctx::clips_handler::~clips_handler() +{ + if (env) { + DestroyEnvironment(env); + } +} + +bool ctx::clips_handler::init(ctx::rule_manager* rm) +{ + rule_mgr = rm; + + int error = init_environment(env); + IF_FAIL_RETURN(error == ERR_NONE, false); + + bool ret = define_global_variable_string("zone", ""); + IF_FAIL_RETURN(ret, false); + + return true; +} + +int ctx::clips_handler::init_environment(void* &environment) +{ + environment = CreateEnvironment(); + if (!environment) { + _E("Create environment failed"); + return ERR_OPERATION_FAILED; + } + + char* func_name = strdup("execute_action"); + char* restrictions = strdup("1s"); + + if (func_name == NULL || restrictions == NULL) { + _E("Memory allocation failed"); + free(func_name); + free(restrictions); + return ERR_OUT_OF_MEMORY; + } + + EnvDefineFunction2(environment, func_name, 'i', PTIEF execute_action, func_name, restrictions); + + free(func_name); + free(restrictions); + + return ERR_NONE; +} + +int ctx::clips_handler::define_template(std::string& script) +{ + IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Deftemplate failed"); + return ERR_NONE; +} + +int ctx::clips_handler::define_class(std::string& script) +{ + IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defclass failed"); + return ERR_NONE; +} + +int ctx::clips_handler::define_rule(std::string& script) +{ + IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defrule failed"); + return ERR_NONE; +} + +int ctx::clips_handler::env_build(std::string& script) +{ + ASSERT_NOT_NULL(env); + if (script.length() == 0) + return ERR_INVALID_PARAMETER; + + _I("EnvBuild script (%s)", script.c_str()); + int ret = EnvBuild(env, script.c_str()); + + return (ret == 1)? ERR_NONE : ERR_OPERATION_FAILED; +} + +int ctx::clips_handler::run_environment() +{ + ASSERT_NOT_NULL(env); + + int fired_rule_num = EnvRun(env, -1); + IF_FAIL_RETURN(fired_rule_num >= 0, ERR_OPERATION_FAILED); + + return ERR_NONE; +} + +int ctx::clips_handler::add_fact(std::string& fact) +{ + ASSERT_NOT_NULL(env); + if (fact.length() == 0) + return ERR_INVALID_PARAMETER; + + void* assert_fact = EnvAssertString(env, fact.c_str()); + IF_FAIL_RETURN_TAG(assert_fact, ERR_OPERATION_FAILED, _E, "Fact assertion failed"); + + return ERR_NONE; +} + +int ctx::clips_handler::route_string_command(std::string& script) +{ + ASSERT_NOT_NULL(env); + if (script.length() == 0) + return ERR_INVALID_PARAMETER; + + int error; + if (RouteCommand(env, script.c_str(), TRUE)){ + _D("Route command succeeded(%s).", script.c_str()); + error = ERR_NONE; + } else { + _E("Route command failed"); + error = ERR_OPERATION_FAILED; + } + + return error; +} + +int ctx::clips_handler::make_instance(std::string& script) +{ + ASSERT_NOT_NULL(env); + if (script.length() == 0) + return ERR_INVALID_PARAMETER; + + int error; + if (EnvMakeInstance(env, script.c_str())){ + _D("Make instance succeeded - %s", script.c_str()); + error = ERR_NONE; + } else { + _E("Make instance failed"); + error = ERR_OPERATION_FAILED; + } + + return error; +} + +int ctx::clips_handler::unmake_instance(std::string& instance_name) +{ + ASSERT_NOT_NULL(env); + if (instance_name.length() == 0) + return ERR_INVALID_PARAMETER; + + void* instance = find_instance_internal(instance_name); + if (!instance) { + _E("Cannot find instance(%s).", instance_name.c_str()); + return ERR_INVALID_PARAMETER; + } + + if (!EnvUnmakeInstance(env, instance)){ + _E("Unmake instance failed"); + return ERR_OPERATION_FAILED; + } + + _D("Unmake instance succeeded(%s).", instance_name.c_str()); + return ERR_NONE; +} + +int ctx::clips_handler::execute_action() +{ + if (!env) { + _E("Environment not created"); + return ERR_OPERATION_FAILED; + } + + const char* result = EnvRtnLexeme(env, 1); + if (!result) { + _E("Failed to get returned rule id"); + return ERR_OPERATION_FAILED; + } + std::string rule_id = result; + std::string id_str = rule_id.substr(4); + + int id = string_to_int(id_str); + rule_mgr->on_rule_triggered(id); + + return ERR_NONE; +} + +bool ctx::clips_handler::find_instance(std::string& instance_name) +{ + ASSERT_NOT_NULL(env); + if (find_instance_internal(instance_name)) { + _D("[%s] already exists", instance_name.c_str()); + return true; + } + + return false; +} + +void* ctx::clips_handler::find_instance_internal(std::string& instance_name) +{ + void* instance = EnvFindInstance(env, NULL, instance_name.c_str(), TRUE); + return instance; +} + +int string_to_int(std::string str) +{ + int i; + std::istringstream convert(str); + + if (!(convert >> i)) + i = 0; + + return i; +} + +bool ctx::clips_handler::define_global_variable_string(std::string variable_name, std::string value) +{ + std::string script = "(defglobal ?*"; + script += variable_name; + script += "* = \""; + script += value; + script += "\")"; + + IF_FAIL_RETURN_TAG(env_build(script) == ERR_NONE, false, _E, "Defglobal failed"); + return true; +} + +bool ctx::clips_handler::set_global_variable_string(std::string variable_name, std::string value) +{ + ASSERT_NOT_NULL(env); + if (variable_name.length() == 0) + return false; + + DATA_OBJECT data; + SetType(data, STRING); + SetValue(data, EnvAddSymbol(env, value.c_str())) ; + + int ret = EnvSetDefglobalValue(env, variable_name.c_str(), &data); + + IF_FAIL_RETURN_TAG(ret == 1, false, _E, "Set global variable(%s) failed", variable_name.c_str()); + return true; +} diff --git a/src/context_trigger/clips_handler.h b/src/context_trigger/clips_handler.h new file mode 100644 index 0000000..a258b13 --- /dev/null +++ b/src/context_trigger/clips_handler.h @@ -0,0 +1,56 @@ +/* +* context-service +* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +#ifndef __CLIPS_HANDLER_H__ +#define __CLIPS_HANDLER_H__ + +namespace ctx { + + class json; + class rule_manager; + + class clips_handler { + public: + clips_handler(); + ~clips_handler(); + bool init(ctx::rule_manager* rm); + + int define_template(std::string& script); + int define_class(std::string& script); + int define_rule(std::string& script); + int run_environment(); + int add_fact(std::string& fact); + int route_string_command(std::string& fact); + int make_instance(std::string& script); + int unmake_instance(std::string& instance_name); + static int execute_action(); + bool find_instance(std::string& instance_name); + bool define_global_variable_string(std::string variable_name, std::string value); + bool set_global_variable_string(std::string variable_name, std::string value); + + private: + int init_environment(void* &environment); + int env_build(std::string& script); + void* find_instance_internal(std::string& instance_name); + + }; /* class clips_handler */ + +} /* namespace ctx */ + +#endif /* End of __CLIPS_HANDLER_H__ */ diff --git a/src/context_trigger/context_monitor.cpp b/src/context_trigger/context_monitor.cpp new file mode 100644 index 0000000..8cfb589 --- /dev/null +++ b/src/context_trigger/context_monitor.cpp @@ -0,0 +1,233 @@ +/* + * context-service + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include "context_monitor.h" +#include "fact_reader.h" +#include "timer_types.h" + +static ctx::fact_reader *reader = NULL; +typedef std::map timer_map_t; +static timer_map_t timer_map; // + +ctx::context_monitor::context_monitor() +{ +} + +ctx::context_monitor::~context_monitor() +{ +} + +bool ctx::context_monitor::init(ctx::fact_reader* fr, ctx::context_trigger* tr) +{ + reader = fr; + trigger = tr; + + return true; +} + +int ctx::context_monitor::subscribe(int rule_id, std::string subject, ctx::json event, const char* zone) +{ + if (subject.compare(TIMER_EVENT_SUBJECT) == 0) { + // option is event json in case of ON_TIME + return subscribe_timer(event, zone); + } + + ctx::json eoption = NULL; + event.get(NULL, CT_RULE_EVENT_OPTION, &eoption); + + int req_id = reader->subscribe(subject.c_str(), &eoption, zone, true); + IF_FAIL_RETURN_TAG(req_id > 0, ERR_OPERATION_FAILED, _E, "Subscribe event failed"); + _D(YELLOW("Subscribe event(rule%d). req%d"), rule_id, req_id); + request_map[rule_id] = req_id; + read_req_cnt_map[req_id]++; + + return ERR_NONE; +} + +int ctx::context_monitor::unsubscribe(int rule_id, std::string subject, ctx::json option, const char* zone) +{ + if (subject.compare(TIMER_EVENT_SUBJECT) == 0) { + return unsubscribe_timer(option, zone); + } + + _D(YELLOW("Unsubscribe event(rule%d). req%d"), rule_id, request_map[rule_id]); + int req_id = request_map[rule_id]; + request_map.erase(rule_id); + + read_req_cnt_map[req_id]--; + if (read_req_cnt_map[req_id] == 0) { + reader->unsubscribe(req_id); + read_req_cnt_map.erase(req_id); + } + + return ERR_NONE; +} + +int ctx::context_monitor::read_time(ctx::json* result, const char* zone) +{ + int dom = ctx::trigger_timer::get_day_of_month(); + (*result).set(NULL, TIMER_RESPONSE_KEY_DAY_OF_MONTH, dom); + + std::string dow = ctx::trigger_timer::get_day_of_week(); + (*result).set(NULL, TIMER_RESPONSE_KEY_DAY_OF_WEEK, dow); + + int time = ctx::trigger_timer::get_minute_of_day(); + (*result).set(NULL, TIMER_RESPONSE_KEY_TIME_OF_DAY, time); + + return ERR_NONE; +} + +int ctx::context_monitor::read(std::string subject, json option, const char* zone, ctx::json* result) +{ + bool ret; + if (subject.compare(TIMER_CONDITION_SUBJECT) == 0) { + return read_time(result, zone); + } + + context_fact fact; + ret = reader->read(subject.c_str(), &option, zone, fact); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Read fact failed"); + + *result = fact.get_data(); + + return ERR_NONE; +} + +static int arrange_day_of_week(ctx::json day_info) +{ + int result = 0; + + std::string key_op; + if (!day_info.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &key_op)) { + result = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\""); + return result; + } + + if (key_op.compare("and") == 0) { + result = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\""); + } + + std::string tmp_d; + for (int i = 0; day_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &tmp_d); i++) { + int dow = ctx::trigger_timer::convert_string_to_day_of_week(tmp_d); + std::string op; + day_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, i, &op); + + if (op.compare("neq") == 0) { + dow = ctx::trigger_timer::convert_string_to_day_of_week("\"" TIMER_EVERYDAY "\"") & ~dow; + } + + if (key_op.compare("and") == 0) { + result &= dow; + } else { + result |= dow; + } + } + _D("Requested day of week (%#x)", result); + + return result; +} + +timer_map_t::iterator ctx::context_monitor::get_zone_timer(std::string zone) +{ + timer_map_t::iterator it = timer_map.find(zone); + + if (it == timer_map.end()) { + timer_map.insert(std::pair(zone, ctx::trigger_timer(trigger, zone))); + } + + return timer_map.find(zone); +} + +int ctx::context_monitor::subscribe_timer(ctx::json option, const char* zone) +{ + ctx::json day_info; + ctx::json time_info; + + ctx::json it; + for (int i = 0; option.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &it); i++){ + std::string key; + it.get(NULL, CT_RULE_DATA_KEY, &key); + + if (key.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) { + day_info = it; + } else if (key.compare(TIMER_RESPONSE_KEY_TIME_OF_DAY) == 0) { + time_info = it; + } + } + + // Day option processing + int dow = arrange_day_of_week(day_info); + + // Time option processing + int time; // minute + timer_map_t::iterator timer = get_zone_timer(zone); + for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) { + (timer->second).add(time, dow); + } + + return ERR_NONE; +} + +int ctx::context_monitor::unsubscribe_timer(ctx::json option, const char* zone) +{ + ctx::json day_info; + ctx::json time_info; + + ctx::json it; + for (int i = 0; option.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &it); i++){ + std::string key; + it.get(NULL, CT_RULE_DATA_KEY, &key); + + if (key.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) { + day_info = it; + } else if (key.compare(TIMER_RESPONSE_KEY_TIME_OF_DAY) == 0) { + time_info = it; + } + } + + // Day option processing + int dow = arrange_day_of_week(day_info); + + // Time option processing + int time; // minute + timer_map_t::iterator timer = get_zone_timer(zone); + for (int i = 0; time_info.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &time); i++) { + (timer->second).remove(time, dow); + } + + if ((timer->second).empty()) { + timer_map.erase(timer); + } + + return ERR_NONE; +} + +bool ctx::context_monitor::is_supported(std::string subject, const char* zone) +{ + if (subject.compare(TIMER_EVENT_SUBJECT) == 0 + || subject.compare(TIMER_CONDITION_SUBJECT) == 0) { + return true; + } + + return reader->is_supported(subject.c_str(), zone); +} diff --git a/src/context_trigger/context_monitor.h b/src/context_trigger/context_monitor.h new file mode 100644 index 0000000..be1695d --- /dev/null +++ b/src/context_trigger/context_monitor.h @@ -0,0 +1,58 @@ +/* +* context-service +* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +#ifndef __CONTEXT_MONITOR_H__ +#define __CONTEXT_MONITOR_H__ + +#include +#include "timer.h" + +namespace ctx { + + class json; + class fact_reader; + class context_fact; + + class context_monitor { + public: + context_monitor(); + ~context_monitor(); + bool init(ctx::fact_reader* fr, ctx::context_trigger* tr); + + int subscribe(int rule_id, std::string subject, ctx::json event, const char* zone); + int unsubscribe(int rule_id, std::string subject, ctx::json option, const char* zone); + int read(std::string subject, json option, const char* zone, ctx::json* result); + bool is_supported(std::string subject, const char* zone); + + private: + std::map::iterator get_zone_timer(std::string zone); + int subscribe_timer(ctx::json option, const char* zone); + int unsubscribe_timer(ctx::json option, const char* zone); + int read_day_of_month(ctx::json* result, const char* zone); + int read_day_of_week(ctx::json* result, const char* zone); + int read_time(ctx::json* result, const char* zone); + std::map request_map; // + std::map read_req_cnt_map; // + ctx::context_trigger* trigger; + + }; /* class context_monitor */ + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_MONITOR_H__ */ diff --git a/src/context_trigger/fact.cpp b/src/context_trigger/fact.cpp new file mode 100644 index 0000000..af83b92 --- /dev/null +++ b/src/context_trigger/fact.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "fact.h" + +ctx::context_fact::context_fact() + : req_id(-1) + , error(ERR_NONE) +{ +} + +ctx::context_fact::context_fact(int id, int err, const char* s, ctx::json& o, ctx::json& d, const char* z) + : req_id(id) + , error(err) + , subject(s) + , option(o) + , data(d) +{ + if (z) { + zone_name = z; + } +} + +ctx::context_fact::~context_fact() +{ +} + +void ctx::context_fact::set_req_id(int id) +{ + req_id = id; +} + +void ctx::context_fact::set_error(int err) +{ + error = err; +} + +void ctx::context_fact::set_subject(const char* s) +{ + subject = s; +} + +void ctx::context_fact::set_option(ctx::json& o) +{ + option = o; +} + +void ctx::context_fact::set_data(ctx::json& d) +{ + data = d; +} + +void ctx::context_fact::set_zone_name(const char* z) +{ + zone_name = z; +} + +int ctx::context_fact::get_req_id() +{ + return req_id; +} + +int ctx::context_fact::get_error() +{ + return error; +} + +const char* ctx::context_fact::get_subject() +{ + return subject.c_str(); +} + +const char* ctx::context_fact::get_zone_name() +{ + return zone_name.c_str(); +} + +ctx::json& ctx::context_fact::get_option() +{ + return option; +} + +ctx::json& ctx::context_fact::get_data() +{ + return data; +} diff --git a/src/context_trigger/fact.h b/src/context_trigger/fact.h new file mode 100644 index 0000000..f961f96 --- /dev/null +++ b/src/context_trigger/fact.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_CONTEXT_TRIGGER_FACT_H__ +#define __CONTEXT_CONTEXT_TRIGGER_FACT_H__ + +#include +#include + +namespace ctx { + + class context_fact { + private: + int req_id; + int error; + std::string subject; + ctx::json option; + ctx::json data; + std::string zone_name; + + public: + context_fact(); + context_fact(int id, int err, const char* s, ctx::json& o, ctx::json& d, const char* z); + ~context_fact(); + + void set_req_id(int id); + void set_error(int err); + void set_subject(const char* s); + void set_option(ctx::json& o); + void set_zone_name(const char* z); + void set_data(ctx::json& d); + + int get_req_id(); + int get_error(); + const char* get_subject(); + const char* get_zone_name(); + ctx::json& get_option(); + ctx::json& get_data(); + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_CONTEXT_TRIGGER_FACT_H__ */ diff --git a/src/context_trigger/fact_reader.cpp b/src/context_trigger/fact_reader.cpp new file mode 100644 index 0000000..fec066f --- /dev/null +++ b/src/context_trigger/fact_reader.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "trigger.h" +#include "fact_request.h" +#include "fact_reader.h" + +#define CLIENT_NAME TRIGGER_CLIENT_NAME +#define COND_END_TIME(T) (g_get_monotonic_time() + (T) * G_TIME_SPAN_SECOND) +#define SUBSCRIBE_TIMEOUT 3 +#define READ_TIMEOUT 10 + +static GMutex request_mutex; +static GCond request_cond; +static int last_rid; +static int last_err; +static ctx::json last_data_read; + +ctx::context_manager_impl *ctx::fact_reader::_context_mgr = NULL; +ctx::context_trigger *ctx::fact_reader::_trigger = NULL; + +static int generate_req_id() +{ + static int req_id = 0; + + if (++req_id < 0) { + // Overflow handling + req_id = 1; + } + + return req_id; +} + +ctx::fact_reader::fact_reader(context_manager_impl* mgr, context_trigger* trigger) +{ + _context_mgr = mgr; + _trigger = trigger; +} + +ctx::fact_reader::~fact_reader() +{ + for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) { + delete *it; + } + subscr_list.clear(); +} + +int ctx::fact_reader::find_sub(const char* subject, json* option, const char* zone) +{ + json opt_j; + if (option) { + opt_j = *option; + } + + for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) { + if ((*it)->subject == subject && (*it)->option == opt_j && (*it)->zone_name == zone) { + return (*it)->sid; + } + } + + return -1; +} + +bool ctx::fact_reader::add_sub(int sid, const char* subject, json* option, const char* zone) +{ + subscr_info_s *info = new(std::nothrow) subscr_info_s(sid, subject, option, zone); + IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed"); + + subscr_list.push_back(info); + return true; +} + +void ctx::fact_reader::remove_sub(const char* subject, json* option, const char* zone) +{ + json opt_j; + if (option) { + opt_j = *option; + } + + for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) { + if ((*it)->subject == subject && (*it)->option == opt_j && (*it)->zone_name == zone) { + delete *it; + subscr_list.erase(it); + return; + } + } +} + +void ctx::fact_reader::remove_sub(int sid) +{ + for (subscr_list_t::iterator it = subscr_list.begin(); it != subscr_list.end(); ++it) { + if ((*it)->sid == sid) { + delete *it; + subscr_list.erase(it); + return; + } + } +} + +gboolean ctx::fact_reader::send_request(gpointer data) +{ + fact_request *req = static_cast(data); + _context_mgr->assign_request(req); + return FALSE; +} + +bool ctx::fact_reader::is_supported(const char* subject, const char* zone) +{ + IF_FAIL_RETURN_TAG(zone, false, _E, "'zone' cannot be NULL"); + return _context_mgr->is_supported(subject, zone); +} + +int ctx::fact_reader::subscribe(const char* subject, json* option, const char* zone, bool wait_response) +{ + IF_FAIL_RETURN_TAG(zone, ERR_INVALID_PARAMETER, _E, "'zone' cannot be NULL"); + IF_FAIL_RETURN(subject, ERR_INVALID_PARAMETER); + + ctx::scope_mutex sm(&request_mutex); + + int rid = find_sub(subject, option, zone); + if (rid > 0) { + _D("Duplicated request for %s", subject); + return rid; + } + + rid = generate_req_id(); + + fact_request *req = new(std::nothrow) fact_request(REQ_SUBSCRIBE, CLIENT_NAME, + rid, subject, option ? option->str().c_str() : NULL, zone, wait_response ? this : NULL); + IF_FAIL_RETURN_TAG(req, -1, _E, "Memory allocation failed"); + + g_idle_add(send_request, req); + add_sub(rid, subject, option, zone); + + IF_FAIL_RETURN_TAG(wait_response, rid, _D, "Ignoring response for %s", subject); + + while (last_rid != rid) { + if (!g_cond_wait_until(&request_cond, &request_mutex, COND_END_TIME(SUBSCRIBE_TIMEOUT))) { + _E("Timeout: subject %s", subject); + //TODO: what happens if the request actually takes more time than the timeout + remove_sub(rid); + return -1; + } + } + + if (last_err != ERR_NONE) { + remove_sub(rid); + _E("Subscription request failed: %#x", last_err); + return -1; + } + + return rid; +} + +void ctx::fact_reader::unsubscribe(const char* subject, json* option, const char* zone) +{ + IF_FAIL_VOID_TAG(zone, _E, "'zone' cannot be NULL"); + IF_FAIL_VOID(subject); + + ctx::scope_mutex sm(&request_mutex); + + int rid = find_sub(subject, option, zone); + IF_FAIL_VOID_TAG(rid > 0, _W, "Unknown subscription for %s", subject); + + unsubscribe(rid); +} + +void ctx::fact_reader::unsubscribe(int subscription_id) +{ + fact_request *req = new(std::nothrow) fact_request(REQ_UNSUBSCRIBE, CLIENT_NAME, subscription_id, "", NULL, NULL, NULL); + IF_FAIL_VOID_TAG(req, _E, "Memory allocation failed"); + + g_idle_add(send_request, req); + remove_sub(subscription_id); +} + +bool ctx::fact_reader::read(const char* subject, json* option, const char* zone, context_fact& fact) +{ + IF_FAIL_RETURN_TAG(zone, false, _E, "'zone' cannot be NULL"); + IF_FAIL_RETURN(subject, false); + + ctx::scope_mutex sm(&request_mutex); + + int rid = generate_req_id(); + + fact_request *req = new(std::nothrow) fact_request(REQ_READ_SYNC, CLIENT_NAME, + rid, subject, option ? option->str().c_str() : NULL, zone, this); + IF_FAIL_RETURN_TAG(req, false, _E, "Memory allocation failed"); + + g_idle_add(send_request, req); + + while (last_rid != rid) { + if (!g_cond_wait_until(&request_cond, &request_mutex, COND_END_TIME(READ_TIMEOUT))) { + _E("Timeout: subject %s", subject); + //TODO: what happens if the request actually takes more time than the timeout + return false; + } + } + + if (last_err != ERR_NONE) { + _E("Read request failed: %#x", last_err); + return false; + } + + fact.set_req_id(rid); + fact.set_subject(subject); + fact.set_data(last_data_read); + if (zone) { + fact.set_zone_name(zone); + } + + last_data_read = EMPTY_JSON_OBJECT; + + return true; +} + +void ctx::fact_reader::reply_result(int req_id, int error, const char* zone, json* request_result, json* fact) +{ + ctx::scope_mutex sm(&request_mutex); + + last_rid = req_id; + last_err = error; + last_data_read = (fact ? *fact : EMPTY_JSON_OBJECT); + + g_cond_signal(&request_cond); +} + +void ctx::fact_reader::publish_fact(int req_id, int error, const char* zone, const char* subject, json* option, json* fact) +{ + _trigger->push_fact(req_id, error, subject, *option, *fact, zone); +} diff --git a/src/context_trigger/fact_reader.h b/src/context_trigger/fact_reader.h new file mode 100644 index 0000000..13f0742 --- /dev/null +++ b/src/context_trigger/fact_reader.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_FACT_READER_H__ +#define __CONTEXT_FACT_READER_H__ + +#include +#include +#include "../context_mgr_impl.h" +#include "fact.h" + +namespace ctx { + + class context_trigger; + + class fact_reader { + public: + fact_reader(context_manager_impl* mgr, context_trigger* trigger); + ~fact_reader(); + + bool is_supported(const char* subject, const char* zone); + + int subscribe(const char* subject, json* option, const char* zone, bool wait_response = false); + void unsubscribe(const char* subject, json* option, const char* zone); + void unsubscribe(int subscription_id); + bool read(const char* subject, json* option, const char* zone, context_fact& fact); + + void reply_result(int req_id, int error, const char* zone, json* request_result = NULL, json* fact = NULL); + void publish_fact(int req_id, int error, const char* zone, const char* subject, json* option, json* fact); + + private: + static context_manager_impl* _context_mgr; + static context_trigger* _trigger; + + struct subscr_info_s { + int sid; + std::string subject; + ctx::json option; + std::string zone_name; + subscr_info_s(int id, const char* subj, ctx::json* opt, const char* zone) + : sid(id), subject(subj) + { + if (opt) + option = *opt; + + if (zone) + zone_name = zone; + } + }; + + typedef std::list subscr_list_t; + subscr_list_t subscr_list; + + int find_sub(const char* subject, json* option, const char* zone); + bool add_sub(int sid, const char* subject, json* option, const char* zone); + void remove_sub(const char* subject, json* option, const char* zone); + void remove_sub(int sid); + + static gboolean send_request(gpointer data); + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_FACT_READER_H__ */ diff --git a/src/context_trigger/fact_request.cpp b/src/context_trigger/fact_request.cpp new file mode 100644 index 0000000..0ae9231 --- /dev/null +++ b/src/context_trigger/fact_request.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "fact_request.h" + +ctx::fact_request::fact_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* zone, fact_reader* reader) + : request_info(type, client, req_id, subj, desc) + , _reader(reader) + , replied(false) +{ + if (zone) + _zone_name = zone; +} + +ctx::fact_request::~fact_request() +{ + reply(ERR_OPERATION_FAILED); +} + +bool ctx::fact_request::reply(int error) +{ + IF_FAIL_RETURN(!replied && _reader, true); + _reader->reply_result(_req_id, error, _zone_name.c_str()); + return (replied = true); +} + +bool ctx::fact_request::reply(int error, ctx::json& request_result) +{ + IF_FAIL_RETURN(!replied && _reader, true); + IF_FAIL_RETURN(_type != REQ_READ_SYNC, true); + _reader->reply_result(_req_id, error, _zone_name.c_str(), &request_result); + return (replied = true); +} + +bool ctx::fact_request::reply(int error, ctx::json& request_result, ctx::json& data_read) +{ + IF_FAIL_RETURN(!replied && _reader, true); + _reader->reply_result(_req_id, error, _zone_name.c_str(), &request_result, &data_read); + return (replied = true); +} + +bool ctx::fact_request::publish(int error, ctx::json& data) +{ + IF_FAIL_RETURN(_reader, true); + _reader->publish_fact(_req_id, error, _zone_name.c_str(), _subject.c_str(), &get_description(), &data); + return true; +} + +void ctx::fact_request::set_zone_name(const char* zone_name) +{ + if (zone_name) + _zone_name = zone_name; +} diff --git a/src/context_trigger/fact_request.h b/src/context_trigger/fact_request.h new file mode 100644 index 0000000..d8d80ba --- /dev/null +++ b/src/context_trigger/fact_request.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_TRIGGER_FACT_REQUEST_H__ +#define __CONTEXT_TRIGGER_FACT_REQUEST_H__ + +#include "fact_reader.h" +#include "../request.h" + +namespace ctx { + + class fact_request : public request_info { + public: + fact_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* zone, fact_reader* reader); + ~fact_request(); + + void set_zone_name(const char* zone_name); + bool reply(int error); + bool reply(int error, ctx::json& request_result); + bool reply(int error, ctx::json& request_result, ctx::json& data_read); + bool publish(int error, ctx::json& data); + + private: + fact_reader *_reader; + bool replied; + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_TRIGGER_FACT_REQUEST_H__ */ diff --git a/src/context_trigger/rule_manager.cpp b/src/context_trigger/rule_manager.cpp new file mode 100644 index 0000000..f9c0d71 --- /dev/null +++ b/src/context_trigger/rule_manager.cpp @@ -0,0 +1,1052 @@ +/* + * context-service + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../access_control/privilege.h" +#include "rule_manager.h" +#include "script_generator.h" +#include "trigger.h" + +#define RULE_TABLE "context_trigger_rule" +#define EVENT_TABLE "context_trigger_event" +#define CONDITION_TABLE "context_trigger_condition" +#define TEMPLATE_TABLE "context_trigger_template" + +#define RULE_TABLE_COLUMNS "enabled INTEGER DEFAULT 0 NOT NULL, creator TEXT DEFAULT '' NOT NULL, description TEXT DEFAULT '', details TEXT DEFAULT '' NOT NULL, zone TEXT DEFAULT '' NOT NULL" +#define EVENT_TABLE_COLUMNS "rule_id INTEGER references context_trigger_rule(row_id) ON DELETE CASCADE NOT NULL, name TEXT DEFAULT '' NOT NULL, instance_name TEXT DEFAULT '', zone TEXT DEFAULT '' NOT NULL" +#define CONDITION_TABLE_COLUMNS "rule_id INTEGER references context_trigger_rule(row_id) ON DELETE CASCADE NOT NULL, name TEXT DEFAULT '' NOT NULL, option TEXT DEFAULT '', instance_name TEXT DEFAULT '', zone TEXT DEFAULT '' NOT NULL" +#define CREATE_TEMPLATE_TABLE "CREATE TABLE IF NOT EXISTS context_trigger_template (name TEXT DEFAULT '' NOT NULL PRIMARY KEY, j_template TEXT DEFAULT '' NOT NULL)" +#define QUERY_TEMPLATE_TABLE "SELECT j_template FROM context_trigger_template" +#define FOREIGN_KEYS_ON "PRAGMA foreign_keys = ON" +#define DELETE_RULE_STATEMENT "DELETE FROM 'context_trigger_rule' where row_id = " +#define UPDATE_RULE_ENABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 1 WHERE row_id = " +#define UPDATE_RULE_DISABLED_STATEMENT "UPDATE context_trigger_rule SET enabled = 0 WHERE row_id = " +#define QUERY_NAME_INSTANCE_NAME_AND_TEMPLATE_BY_RULE_ID_STATEMENT "SELECT context_trigger_condition.name, instance_name, j_template as templates FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id = " +#define QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT "SELECT DISTINCT context_trigger_condition.name, instance_name, option, j_template FROM context_trigger_condition JOIN context_trigger_template ON (context_trigger_condition.name = context_trigger_template.name) WHERE rule_id IN (SELECT row_id FROM context_trigger_rule WHERE enabled = 1 AND row_id IN (SELECT rule_id FROM context_trigger_event WHERE context_trigger_event.instance_name = '" +#define QUERY_RULE_AND_ZONE_BY_RULE_ID "SELECT details, zone FROM context_trigger_rule WHERE row_id = " +#define QUERY_EVENT_TEMPLATE_BY_RULE_ID "SELECT j_template FROM context_trigger_template WHERE name IN (SELECT name FROM context_trigger_event WHERE rule_id = " +#define QUERY_CONDITION_BY_RULE_ID "SELECT name, option FROM context_trigger_condition WHERE rule_id = " + +#define INSTANCE_NAME_DELIMITER "/" +#define EVENT_KEY_PREFIX "?" + +static ctx::context_trigger* trigger = NULL; + +static int string_to_int(std::string str) +{ + int i; + std::istringstream convert(str); + + if (!(convert >> i)) + i = 0; + + return i; +} + +static std::string int_to_string(int i) +{ + std::ostringstream convert; + convert << i; + std::string str = convert.str(); + return str; +} + +ctx::rule_manager::rule_manager() +{ +} + +ctx::rule_manager::~rule_manager() +{ +} + +bool ctx::rule_manager::init(ctx::context_trigger* tr, ctx::fact_reader* fr) +{ + int error; + bool ret; + + trigger = tr; + + ret = clips_h.init(this); + IF_FAIL_RETURN_TAG(ret, false, _E, "CLIPS handler initialization failed"); + + ret = c_monitor.init(fr, tr); + + // Create tables into db (rule, event, condition, action, template) + ret = db_manager::create_table(1, RULE_TABLE, RULE_TABLE_COLUMNS, NULL, NULL); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create rule table failed"); + + ret = db_manager::create_table(2, EVENT_TABLE, EVENT_TABLE_COLUMNS, NULL, NULL); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create event table failed"); + + ret = db_manager::create_table(3, CONDITION_TABLE, CONDITION_TABLE_COLUMNS, NULL, NULL); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create condition table failed"); + + ret = db_manager::execute(4, CREATE_TEMPLATE_TABLE, NULL); + IF_FAIL_RETURN_TAG(ret, false, _E, "Create template table failed"); + + // Load all templates from DB + std::vector record; + ret = db_manager::execute_sync(QUERY_TEMPLATE_TABLE, &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query template table failed"); + + // Make scripts for deftemplate, defclass, make-instance and load them to clips + std::vector::iterator vec_end = record.end(); + for (std::vector::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + std::string tmpl_str; + elem.get(NULL, "j_template", &tmpl_str); + ctx::json tmpl = tmpl_str; + + std::string deftemplate_str = script_generator::generate_deftemplate(&tmpl); + error = clips_h.define_template(deftemplate_str); + IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Deftemplate failed"); + + std::string defclass_str = script_generator::generate_defclass(&tmpl); + error = clips_h.define_class(defclass_str); + IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Defclass failed"); + + std::string makeinstance_str = script_generator::generate_makeinstance(&tmpl); + error = clips_h.make_instance(makeinstance_str); + IF_FAIL_RETURN_TAG(error == ERR_NONE, false, _E, "Makeinstance failed"); + } + + // Foreign keys on + ret = db_manager::execute_sync(FOREIGN_KEYS_ON, &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Foreign keys on failed"); + + ret = reenable_rule(); + + return ret; +} + +bool ctx::rule_manager::reenable_rule(void) +{ + int error; + std::string q = "SELECT row_id FROM context_trigger_rule where enabled = 1"; + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_ids of enabled rules failed"); + + std::vector::iterator vec_end = record.end(); + for (std::vector::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + int row_id; + elem.get(NULL, "row_id", &row_id); + + error = enable_rule(row_id); + if (error != ERR_NONE) { + _E("Re-enable rule%d failed(%d)", row_id, error); + } else { + _E("Re-enable rule%d succeeded", row_id); + } + } + + return true; +} + +bool ctx::rule_manager::rule_data_arr_elem_equals(ctx::json& lelem, ctx::json& relem) +{ + std::string lkey, rkey; + lelem.get(NULL, CT_RULE_DATA_KEY, &lkey); + relem.get(NULL, CT_RULE_DATA_KEY, &rkey); + if (lkey.compare(rkey)) + return false; + + int lvc, rvc, lvoc, rvoc; + lvc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR); + rvc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR); + lvoc = lelem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR); + rvoc = relem.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR); + if (!((lvc == rvc) && (lvc == lvoc) && (lvc && rvoc))) + return false; + + if (lvc > 1) { + std::string lop, rop; + lelem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &lop); + relem.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &rop); + if (lop.compare(rop)) + return false; + } + + for (int i = 0; i < lvc; i++) { + bool found = false; + std::string lv, lvo; + lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, i, &lv); + lelem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, i, &lvo); + + for (int j = 0; j < lvc; j++) { + std::string rv, rvo; + relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, j, &rv); + relem.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, j, &rvo); + + if (!lv.compare(rv) && !lvo.compare(rvo)) { + found = true; + break; + } + } + if (!found) + return false; + } + + return true; +} + +bool ctx::rule_manager::rule_item_equals(ctx::json& litem, ctx::json& ritem) +{ + // Compare item name + std::string lei, rei; + litem.get(NULL, CT_RULE_EVENT_ITEM, &lei); + ritem.get(NULL, CT_RULE_EVENT_ITEM, &rei); + if (lei.compare(rei)) + return false; + + // Compare option + ctx::json loption, roption; + std::string linst, rinst; + litem.get(NULL, CT_RULE_EVENT_OPTION, &loption); + ritem.get(NULL, CT_RULE_EVENT_OPTION, &roption); + linst = get_instance_name(lei, loption); + rinst = get_instance_name(rei, roption); + if (linst.compare(rinst)) + return false; + + int ledac, redac; + ledac = litem.array_get_size(NULL, CT_RULE_DATA_ARR); + redac = ritem.array_get_size(NULL, CT_RULE_DATA_ARR); + if (ledac != redac) + return false; + + // Compare item operator; + if (ledac > 1 ) { + std::string leop, reop; + litem.get(NULL, CT_RULE_EVENT_OPERATOR, &leop); + ritem.get(NULL, CT_RULE_EVENT_OPERATOR, &reop); + if (leop.compare(reop)) + return false; + } + + for (int i = 0; i < ledac; i++) { + bool found = false; + ctx::json lelem; + litem.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &lelem); + + for (int j = 0; j < ledac; j++) { + ctx::json relem; + ritem.get_array_elem(NULL, CT_RULE_DATA_ARR, j, &relem); + + if (rule_data_arr_elem_equals(lelem, relem)) { + found = true; + break; + } + } + if (!found) + return false; + } + + return true; +} + +bool ctx::rule_manager::rule_equals(ctx::json& lrule, ctx::json& rrule) +{ + // Compare event + ctx::json le, re; + lrule.get(NULL, CT_RULE_EVENT, &le); + rrule.get(NULL, CT_RULE_EVENT, &re); + if (!rule_item_equals(le, re)) + return false; + + // Compare conditions + int lcc, rcc; + lcc = lrule.array_get_size(NULL, CT_RULE_CONDITION); + rcc = rrule.array_get_size(NULL, CT_RULE_CONDITION); + if (lcc != rcc) + return false; + + if (lcc > 1) { + std::string lop, rop; + lrule.get(NULL, CT_RULE_OPERATOR, &lop); + rrule.get(NULL, CT_RULE_OPERATOR, &rop); + if (lop.compare(rop)) + return false; + } + + for (int i = 0; i < lcc; i++) { + bool found = false; + ctx::json lc; + lrule.get_array_elem(NULL, CT_RULE_CONDITION, i, &lc); + + for (int j = 0; j < lcc; j++) { + ctx::json rc; + rrule.get_array_elem(NULL, CT_RULE_CONDITION, j, &rc); + + if (rule_item_equals(lc, rc)) { + found = true; + break; + } + } + if (!found) + return false; + } + + // Compare action + std::string laction, raction; + lrule.get(NULL, CT_RULE_ACTION, &laction); + rrule.get(NULL, CT_RULE_ACTION, &raction); + if (laction.compare(raction)) + return false; + + return true; +} + +int64_t ctx::rule_manager::get_duplicated_rule(std::string creator, std::string zone, ctx::json& rule) +{ + std::string q = "SELECT row_id, details FROM context_trigger_rule WHERE creator = '"; + q += creator; + q += "' AND zone ='"; + q += zone; + q += "'"; + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query row_id, details by creator failed"); + + ctx::json r_details; + rule.get(NULL, CT_RULE_DETAILS, &r_details); + std::vector::iterator vec_end = record.end(); + + for (std::vector::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + std::string details; + ctx::json d_details; + + elem.get(NULL, "details", &details); + d_details = details; + + if (rule_equals(r_details, d_details)) { + int64_t row_id; + elem.get(NULL, "row_id", &row_id); + return row_id; + } + } + + return -1; +} + +int ctx::rule_manager::verify_rule(ctx::json& rule, const char* app_id, const char* zone) +{ + ctx::json details; + rule.get(NULL, CT_RULE_DETAILS, &details); + + std::string e_name; + rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name); + + IF_FAIL_RETURN_TAG(c_monitor.is_supported(e_name, zone), ERR_NOT_SUPPORTED, _I, "Event(%s) is not supported", e_name.c_str()); + + if (app_id) { + if (!ctx::privilege_manager::is_allowed(app_id, e_name.c_str())) { + _W("Permission denied for '%s'", e_name.c_str()); + return ERR_PERMISSION_DENIED; + } + } + + ctx::json it; + for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){ + std::string c_name; + it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name); + + IF_FAIL_RETURN_TAG(c_monitor.is_supported(c_name, zone), ERR_NOT_SUPPORTED, _I, "Condition(%s) is not supported", c_name.c_str()); + + if (!ctx::privilege_manager::is_allowed(app_id, c_name.c_str())) { + _W("Permission denied for '%s'", c_name.c_str()); + return ERR_PERMISSION_DENIED; + } + } + + return ERR_NONE; +} + +int ctx::rule_manager::add_rule(std::string creator, ctx::json rule, std::string zone, ctx::json* rule_id) +{ + // * Insert rule to DB + bool ret; + int64_t rid; + + // Check if all items are supported && allowed to access + int err = verify_rule(rule, creator.c_str(), zone.c_str()); + IF_FAIL_RETURN(err==ERR_NONE, err); + + // Check if duplicated rule exits + if ((rid = get_duplicated_rule(creator, zone, rule)) > 0) { + // Save rule id + rule_id->set(NULL, CT_RULE_ID, rid); + return ERR_NONE; + } + + // Insert rule to rule table, get rule id and save it to json parameter + ctx::json r_record; + std::string description; + ctx::json details; + rule.get(NULL, CT_RULE_DESCRIPTION, &description); + rule.get(NULL, CT_RULE_DETAILS, &details); + r_record.set(NULL, "creator", creator); + r_record.set(NULL, "description", description); + r_record.set(NULL, "details", details.str()); + r_record.set(NULL, "zone", zone); + ret = db_manager::insert_sync(RULE_TABLE, r_record, &rid); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert rule to db failed"); + + // Save rule id + rule_id->set(NULL, CT_RULE_ID, rid); + + // Insert event & conditions of a rule into each table + ctx::json e_record; + std::string e_name; + ctx::json e_option_j; + std::string e_inst; + + rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_ITEM, &e_name); + rule.get(CT_RULE_DETAILS "." CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &e_option_j); + e_inst = get_instance_name(e_name, e_option_j); + + e_record.set(NULL, "rule_id", rid); + e_record.set(NULL, "name", e_name); + e_record.set(NULL, "instance_name", e_inst); + e_record.set(NULL, "zone", zone); + ret = db_manager::insert(1, EVENT_TABLE, e_record, NULL); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert event to db failed"); + + ctx::json it; + for (int i = 0; rule.get_array_elem(CT_RULE_DETAILS, CT_RULE_CONDITION, i, &it); i++){ + ctx::json c_record; + std::string c_name; + ctx::json c_option; + char* c_option_str; + ctx::json tmp_option; + std::string c_inst; + + it.get(NULL, CT_RULE_CONDITION_ITEM, &c_name); + it.get(NULL, CT_RULE_CONDITION_OPTION, &tmp_option); + c_inst = get_instance_name(c_name, tmp_option); + c_option.set(NULL, CT_RULE_CONDITION_OPTION, tmp_option); + c_option_str = c_option.dup_cstr(); + + c_record.set(NULL, "rule_id", rid); + c_record.set(NULL, "name", c_name); + c_record.set(NULL, "option", (c_option_str)? c_option_str : ""); + c_record.set(NULL, "instance_name", c_inst); + c_record.set(NULL, "zone", zone); + + ret = db_manager::insert(2, CONDITION_TABLE, c_record, NULL); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Insert conditions to db failed"); + + free(c_option_str); + } + + _D("Add rule%d succeeded", (int)rid); + return ERR_NONE; +} + + +int ctx::rule_manager::remove_rule(int rule_id) +{ + // Delete rule from DB + bool ret; + + std::string query = DELETE_RULE_STATEMENT; + query += int_to_string(rule_id); + std::vector record; + ret = db_manager::execute_sync(query.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Remove rule from db failed"); + + return ERR_NONE; +} + + +int ctx::rule_manager::enable_rule(int rule_id) +{ + // Subscribe event + bool ret; + int error; + std::string id_str = int_to_string(rule_id); + + // Get rule json by rule id; + std::string q1 = QUERY_RULE_AND_ZONE_BY_RULE_ID; + q1 += int_to_string(rule_id); + std::vector rule_record; + ret = db_manager::execute_sync(q1.c_str(), &rule_record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rule by rule id failed"); + std::string r1; + std::string zone; + rule_record[0].get(NULL, "details", &r1); + rule_record[0].get(NULL, "zone", &zone); + ctx::json rule = r1; + ctx::json event; + rule.get(NULL, CT_RULE_EVENT, &event); + + // Get event template by rule id + std::string q2 = QUERY_EVENT_TEMPLATE_BY_RULE_ID; + q2 += int_to_string(rule_id); + q2 += ")"; + std::vector etemplate_record; + ret = db_manager::execute_sync(q2.c_str(), &etemplate_record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query event template by rule id failed"); + std::string r2; + etemplate_record[0].get(NULL, "j_template", &r2); + ctx::json etemplate = r2; + + // Query name, instance name & template for conditions of the rule + std::string q3 = QUERY_NAME_INSTANCE_NAME_AND_TEMPLATE_BY_RULE_ID_STATEMENT; + q3 += id_str; + std::vector cond_record; + ret = db_manager::execute_sync(q3.c_str(), &cond_record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query condition's names, instance names, templates by rule id failed"); + + ctx::json inst_names; + std::vector::iterator vec_end = cond_record.end(); + for (std::vector::iterator vec_pos = cond_record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + + std::string cname; + std::string ciname; + std::string temp; + elem.get(NULL, "name", &cname); + elem.get(NULL, "instance_name", &ciname); + elem.get(NULL, "templates", &temp); + ctx::json ctemplate = temp; + ctemplate.set(NULL, "instance_name", ciname); + // TODO ctemplate (name, instance_name, attributes) now - it is based on form using when templates are inserted into db when rebooting + + // For defrule script generation + inst_names.set(NULL, cname.c_str(), ciname); + + if (cname.compare(ciname) != 0) { + if (!clips_h.find_instance(ciname)) { + std::string makeinst_script = script_generator::generate_makeinstance(&ctemplate); + error = clips_h.make_instance(makeinst_script); + IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Add condition instance([%s]) failed", ciname.c_str()); + + cond_cnt_map[ciname] = 1; + } else { + cond_cnt_map[ciname]++; + } + } + } + + // Subscribe event + std::string ename; + etemplate.get(NULL, "name", &ename); + + //TODO: set the proper zone + error = c_monitor.subscribe(rule_id, ename, event, zone.c_str()); + IF_FAIL_RETURN(error == ERR_NONE, ERR_OPERATION_FAILED); + + // Generate defrule script and execute it + std::string script = script_generator::generate_defrule(id_str, etemplate, rule, &inst_names, zone); + error = clips_h.define_rule(script); + IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Defrule failed"); + + // Update db to set 'enabled' + std::string q4 = UPDATE_RULE_ENABLED_STATEMENT; + q4 += id_str; + std::vector record; + ret = db_manager::execute_sync(q4.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed"); + + _D(YELLOW("Enable Rule%d succeeded"), rule_id); + + return ERR_NONE; +} + +std::string ctx::rule_manager::get_instance_name(std::string name, ctx::json& option) +{ + std::string inst_name = name; + + // Get template for the option + std::string q = "SELECT j_template FROM context_trigger_template WHERE name = '"; + q += name; + q += "'"; + std::vector template_record; + db_manager::execute_sync(q.c_str(), &template_record); + + std::string ct_str; + template_record[0].get(NULL, "j_template", &ct_str); + ctx::json j_template = ct_str; + + std::string option_key; + for (int i = 0; j_template.get_array_elem(NULL, "option", i, &option_key); i++) { + std::string val_str; + int val; + if (option.get(NULL, option_key.c_str(), &val_str)) { + inst_name += INSTANCE_NAME_DELIMITER; + inst_name += val_str; + } else if (option.get(NULL, option_key.c_str(), &val)) { + inst_name += INSTANCE_NAME_DELIMITER; + inst_name += int_to_string(val); + } else { + inst_name += INSTANCE_NAME_DELIMITER; + } + } + + return inst_name; +} + +int ctx::rule_manager::disable_rule(int rule_id) +{ + int error; + bool ret; + + // For event with options + // Get rule json by rule id; + std::string q1 = QUERY_RULE_AND_ZONE_BY_RULE_ID; + q1 += int_to_string(rule_id); + std::vector rule_record; + ret = db_manager::execute_sync(q1.c_str(), &rule_record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rule by rule id failed"); + std::string r1; + std::string zone; + rule_record[0].get(NULL, "details", &r1); + rule_record[0].get(NULL, "zone", &zone); + ctx::json rule = r1; + ctx::json event; + rule.get(NULL, CT_RULE_EVENT, &event); + std::string ename; + event.get(NULL, CT_RULE_EVENT_ITEM, &ename); + + // Unsubscribe event + error = c_monitor.unsubscribe(rule_id, ename, event, zone.c_str()); + IF_FAIL_RETURN(error == ERR_NONE, ERR_OPERATION_FAILED); + + // Undef rule in clips + std::string id_str = int_to_string(rule_id); + std::string script = script_generator::generate_undefrule(id_str); + error = clips_h.route_string_command(script); + IF_FAIL_RETURN_TAG(error == ERR_NONE, ERR_OPERATION_FAILED, _E, "Undefrule failed"); + + // Update db to set 'disabled' + std::string q2 = UPDATE_RULE_DISABLED_STATEMENT; + q2 += id_str; + std::vector record; + ret = db_manager::execute_sync(q2.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Update db failed"); + + // Remove condition instances TODO + std::string q3 = "SELECT name, instance_name FROM context_trigger_condition WHERE rule_id = "; + q3 += id_str; + std::vector name_record; + ret = db_manager::execute_sync(q3.c_str(), &name_record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query condition's name, instance names by rule id failed"); + + std::vector::iterator vec_end = name_record.end(); + for (std::vector::iterator vec_pos = name_record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + + std::string cname; + std::string ciname; + elem.get(NULL, "name", &cname); + elem.get(NULL, "instance_name", &ciname); + + if (cname.compare(ciname) != 0) { + cond_cnt_map[ciname]--; + + if (cond_cnt_map[ciname] == 0) { + error = clips_h.unmake_instance(ciname); + IF_FAIL_RETURN(error == ERR_NONE, error); + + cond_cnt_map.erase(ciname); + } + } + } + + return ERR_NONE; +} + +void ctx::rule_manager::make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption) +{ + std::string option_key; + for (int i = 0; ctemplate.get_array_elem(NULL, "option", i, &option_key); i++) { + std::string coption_valstr; + if (coption->get(NULL, option_key.c_str(), &coption_valstr)) { + if (coption_valstr.find(EVENT_KEY_PREFIX) == 0) { + std::string event_key = coption_valstr.substr(1, coption_valstr.length() - 1); + + std::string e_valstr; + int e_val; + if (edata.get(NULL, event_key.c_str(), &e_valstr)) { + coption->set(NULL, option_key.c_str(), e_valstr); + } else if (edata.get(NULL, event_key.c_str(), &e_val)) { + coption->set(NULL, option_key.c_str(), e_val); + } + } + } + } +} + +void ctx::rule_manager::on_event_received(std::string item, ctx::json option, ctx::json data, std::string zone) +{ + _D(YELLOW("Event(%s(%s) - %s) is invoked."), item.c_str(), option.str().c_str(), data.str().c_str()); + // TODO: Check permission of an event(item), if permission denied, return + + int err; + bool ret; + // Set the zone + ret = clips_h.set_global_variable_string("zone", zone); + if (!ret) { + _E("Set clips zone to %s failed", zone.c_str()); + return; + } + + // Generate event fact script + std::string q1 = "SELECT j_template FROM context_trigger_template WHERE name = '"; + q1 += item; + q1 += "'"; + std::vector etemplate_record; + db_manager::execute_sync(q1.c_str(), &etemplate_record); + std::string r1; + etemplate_record[0].get(NULL, "j_template", &r1); + ctx::json etemplate = r1; + + std::string eventfact_str = script_generator::generate_fact(item, etemplate, option, data); + + // Get Conditions template of invoked event (db query) + std::string e_inst = get_instance_name(item, option); + std::string query = QUERY_CONDITION_TEMPLATES_OF_INVOKED_EVENT_STATEMENT; + query += e_inst; + query += "' AND zone = '"; + query += zone; + query += "'))"; + std::vector conds; + ret = db_manager::execute_sync(query.c_str(), &conds); + IF_FAIL_VOID_TAG(ret, _E, "Query condition templates of invoked event failed"); + + int cond_num = conds.size(); + for (int i = 0; i < cond_num; i++) { + std::string temp; + conds[i].get(NULL, "j_template", &temp); + ctx::json ctemplate = temp; + + std::string cname; + conds[i].get(NULL, "name", &cname); + + std::string ciname; + conds[i].get(NULL, "instance_name", &ciname); + + std::string coption_str; + conds[i].get(NULL, "option", &coption_str); + ctx::json coption = NULL; + if (!coption_str.empty()) { + ctx::json coption_tmp = coption_str; + coption_tmp.get(NULL, CT_RULE_CONDITION_OPTION, &coption); + } + + // Check if the condition uses event data key as an option + if (ciname.find(EVENT_KEY_PREFIX) != std::string::npos) { + make_condition_option_based_on_event_data(ctemplate, data, &coption); + } + + // TODO: Check permission of a condition(cname), if permission granted, read condition data. (or, condition data should be empty json) + + // Get Context Data and Set the proper zone. + ctx::json condition_data; + err = c_monitor.read(cname, coption, zone.c_str(), &condition_data); + if (err != ERR_NONE) + return; + _D(YELLOW("Condition(%s(%s) - %s)."), cname.c_str(), coption.str().c_str(), condition_data.str().c_str()); + + // Generate ModifyInstance script + std::string modifyinst_script = script_generator::generate_modifyinstance(ciname, ctemplate, condition_data); + + err = clips_h.route_string_command(modifyinst_script); + IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Modify condition instance failed"); + } + + // Add fact and Run environment + err = clips_h.add_fact(eventfact_str); + IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Assert event fact failed"); + + err = clips_h.run_environment(); + IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Run environment failed"); + + // Retract event fact + std::string retract_command = "(retract *)"; + err = clips_h.route_string_command(retract_command); + IF_FAIL_VOID_TAG(err == ERR_NONE, _E, "Retract event fact failed"); +} + +static void trigger_action_app_control(ctx::json& action, std::string zone) +{ + int error; + std::string appctl_str; + action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str); + + char* str = static_cast(malloc(appctl_str.length())); + if (str == NULL) { + _E("Memory allocation failed"); + return; + } + appctl_str.copy(str, appctl_str.length(), 0); + bundle_raw* encoded = reinterpret_cast(str); + bundle* appctl_bundle = bundle_decode(encoded, appctl_str.length()); + + app_control_h app = NULL; + app_control_create(&app); + app_control_import_from_bundle(app, appctl_bundle); + + char* op; + app_control_get_operation(app, &op); + app_control_set_operation(app, APP_SVC_OPERATION_JUMP); + app_control_add_extra_data(app, APP_SVC_K_JUMP_ZONE_NAME, zone.c_str()); + if (op != NULL) { + app_control_add_extra_data(app, APP_SVC_K_JUMP_ORIGIN_OPERATION, op); + } + + error = app_control_send_launch_request(app, NULL, NULL); + if (error != APP_CONTROL_ERROR_NONE) { + _E("Launch request failed(%d)", error); + } else { + _D("Launch request succeeded"); + } + bundle_free(appctl_bundle); + free(str); + app_control_destroy(app); +} + +static void trigger_action_notification(ctx::json& action, std::string creator, std::string zone) +{ + int error; + notification_h notification = notification_create(NOTIFICATION_TYPE_NOTI); + std::string title; + if (action.get(NULL, CT_RULE_ACTION_NOTI_TITLE, &title)) { + error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_TITLE, title.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set notification title failed(%d)", error); + } + } + + std::string content; + if (action.get(NULL, CT_RULE_ACTION_NOTI_CONTENT, &content)) { + error = notification_set_text(notification, NOTIFICATION_TEXT_TYPE_CONTENT, content.c_str(), NULL, NOTIFICATION_VARIABLE_TYPE_NONE); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set notification contents failed(%d)", error); + } + } + + std::string image_path; + if (action.get(NULL, CT_RULE_ACTION_NOTI_ICON_PATH, &image_path)) { + error = notification_set_image(notification, NOTIFICATION_IMAGE_TYPE_ICON, image_path.c_str()); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set notification icon image failed(%d)", error); + } + } + + std::string appctl_str; + char* str = NULL; + bundle_raw* encoded = NULL; + bundle* appctl_bundle = NULL; + app_control_h app = NULL; + if (action.get(NULL, CT_RULE_ACTION_APP_CONTROL, &appctl_str)) { + str = static_cast(malloc(appctl_str.length())); + if (str == NULL) { + _E("Memory allocation failed"); + notification_free(notification); + return; + } + appctl_str.copy(str, appctl_str.length(), 0); + encoded = reinterpret_cast(str); + appctl_bundle = bundle_decode(encoded, appctl_str.length()); + + app_control_create(&app); + app_control_import_from_bundle(app, appctl_bundle); + + error = notification_set_launch_option(notification, NOTIFICATION_LAUNCH_OPTION_APP_CONTROL, app); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set launch option failed(%d)", error); + } + } + + error = notification_set_pkgname(notification, creator.c_str()); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Set pkgname(%s) failed(%d)", creator.c_str(), error); + } + + ctx::scope_zone_joiner sz(zone.c_str()); + + error = notification_post(notification); + if (error != NOTIFICATION_ERROR_NONE) { + _E("Post notification failed(%d)", error); + } else { + _D("Post notification succeeded"); + } + + bundle_free(appctl_bundle); + free(str); + notification_free(notification); + if (app) { + app_control_destroy(app); + } +} + +void ctx::rule_manager::on_rule_triggered(int rule_id) +{ + _D(YELLOW("Rule%d is triggered"), rule_id); + + std::string q = "SELECT details, creator, zone FROM context_trigger_rule WHERE row_id ="; + q += int_to_string(rule_id); + + std::vector record; + db_manager::execute_sync(q.c_str(), &record); + + std::string details_str; + record[0].get(NULL, "details", &details_str); + ctx::json details = details_str; + ctx::json action; + details.get(NULL, CT_RULE_ACTION, &action); + std::string zone; + record[0].get(NULL, "zone", &zone); + + std::string type; + if (action.get(NULL, CT_RULE_ACTION_TYPE, &type)) { + if (type.compare(CT_RULE_ACTION_TYPE_APP_CONTROL) == 0) { + trigger_action_app_control(action, zone); + } else if (type.compare(CT_RULE_ACTION_TYPE_NOTIFICATION) == 0) { + std::string creator; + record[0].get(NULL, "creator", &creator); + trigger_action_notification(action, creator, zone); + } + } +} + +int ctx::rule_manager::check_rule(std::string creator, int rule_id) +{ + // Get creator app id + std::string q = "SELECT creator FROM context_trigger_rule WHERE row_id ="; + q += int_to_string(rule_id); + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query creator by rule id failed"); + + if (record.size() == 0) { + return ERR_NO_DATA; + } + + std::string c; + record[0].get(NULL, "creator", &c); + + if (c.compare(creator) == 0){ + return ERR_NONE; + } + + return ERR_NO_DATA; +} + +bool ctx::rule_manager::is_rule_enabled(int rule_id) +{ + std::string q = "SELECT enabled FROM context_trigger_rule WHERE row_id ="; + q += int_to_string(rule_id); + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query enabled by rule id failed"); + + int enabled; + record[0].get(NULL, "enabled", &enabled); + + if (enabled == 1) + return true; + else + return false; +} + +int ctx::rule_manager::get_rule_by_id(std::string creator, int rule_id, ctx::json* request_result) +{ +// std::string q = "SELECT description, details FROM context_trigger_rule WHERE (creator = '"; + std::string q = "SELECT description FROM context_trigger_rule WHERE (creator = '"; + q += creator; + q += "') and (row_id = "; + q += int_to_string(rule_id); + q += ")"; + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, false, _E, "Query rule by rule id failed"); + + if (record.size() == 0) { + return ERR_NO_DATA; + } else if (record.size() != 1) { + return ERR_OPERATION_FAILED; + } + + std::string description; +// std::string details; + record[0].get(NULL, "description", &description); +// record[0].get(NULL, "details", &details); + + (*request_result).set(NULL, CT_RULE_ID, rule_id); + (*request_result).set(NULL, CT_RULE_DESCRIPTION, description); +// (*request_result).set(NULL, CT_RULE_DETAILS, details); + + return ERR_NONE; +} + +int ctx::rule_manager::get_rule_ids(std::string creator, ctx::json* request_result) +{ + (*request_result) = "{ \"" CT_RULE_ARRAY_ENABLED "\" : [ ] , \"" CT_RULE_ARRAY_DISABLED "\" : [ ] }"; + + std::string q = "SELECT row_id, enabled FROM context_trigger_rule WHERE (creator = '"; + q += creator; + q += "')"; + + std::vector record; + bool ret = db_manager::execute_sync(q.c_str(), &record); + IF_FAIL_RETURN_TAG(ret, ERR_OPERATION_FAILED, _E, "Query rules failed"); + + std::vector::iterator vec_end = record.end(); + for (std::vector::iterator vec_pos = record.begin(); vec_pos != vec_end; ++vec_pos) { + ctx::json elem = *vec_pos; + std::string id; + int enabled; + + elem.get(NULL, "row_id", &id); + elem.get(NULL, "enabled", &enabled); + + if (enabled == 1) { + (*request_result).array_append(NULL, CT_RULE_ARRAY_ENABLED, string_to_int(id)); + } else if (enabled == 0) { + (*request_result).array_append(NULL, CT_RULE_ARRAY_DISABLED, string_to_int(id)); + } + } + + return ERR_NONE; +} diff --git a/src/context_trigger/rule_manager.h b/src/context_trigger/rule_manager.h new file mode 100644 index 0000000..e6b1557 --- /dev/null +++ b/src/context_trigger/rule_manager.h @@ -0,0 +1,68 @@ +/* +* context-service +* +* Copyright (c) 2014 Samsung Electronics Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +*/ + +#ifndef __RULE_MANAGER_H__ +#define __RULE_MANAGER_H__ + +#include "clips_handler.h" +#include "context_monitor.h" + +namespace ctx { + + class json; + class context_trigger; + class fact_reader; + + class rule_manager { + public: + rule_manager(); + ~rule_manager(); + bool init(ctx::context_trigger* tr, ctx::fact_reader* fr); + int add_rule(std::string creator, ctx::json rule, std::string zone, ctx::json* rule_id); + int remove_rule(int rule_id); + int enable_rule(int rule_id); + int disable_rule(int rule_id); + int get_rule_by_id(std::string creator, int rule_id, ctx::json* request_result); + int get_rule_ids(std::string creator, ctx::json* request_result); + int check_rule(std::string creator, int rule_id); + bool is_rule_enabled(int rule_id); + + void on_event_received(std::string item, ctx::json option, ctx::json data, std::string zone); + void on_rule_triggered(int rule_id); + + private: + clips_handler clips_h; + context_monitor c_monitor; + + bool reenable_rule(void); + int verify_rule(ctx::json& rule, const char* app_id, const char* zone); + int64_t get_duplicated_rule(std::string creator, std::string zone, ctx::json& rule); + bool rule_data_arr_elem_equals(ctx::json& lelem, ctx::json& relem); + bool rule_item_equals(ctx::json& litem, ctx::json& ritem); + bool rule_equals(ctx::json& lrule, ctx::json& rrule); + std::string get_instance_name(std::string name, ctx::json& condition); + void make_condition_option_based_on_event_data(ctx::json& ctemplate, ctx::json& edata, ctx::json* coption); + + std::map cond_cnt_map; // + + }; /* class rule_manager */ + +} /* namespace ctx */ + +#endif /* End of __RULE_MANAGER_H__ */ diff --git a/src/context_trigger/script_generator.cpp b/src/context_trigger/script_generator.cpp new file mode 100644 index 0000000..ade39ef --- /dev/null +++ b/src/context_trigger/script_generator.cpp @@ -0,0 +1,516 @@ +/* + * context-service + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "script_generator.h" +#include "timer_types.h" + +#define EVENT_WEEKDAY "(or (eq ?DayOfWeek \"Mon\") (eq ?DayOfWeek \"Tue\") (eq ?DayOfWeek \"Wed\") (eq ?DayOfWeek \"Thu\") (eq ?DayOfWeek \"Fri\"))" +#define EVENT_WEEKEND "(or (eq ?DayOfWeek \"Sat\") (eq ?DayOfWeek \"Sun\"))" +#define CONDITION_WEEKDAY "(or (eq (send [%s] get-DayOfWeek) \"Mon\") (eq (send [%s] get-DayOfWeek) \"Tue\") (eq (send [%s] get-DayOfWeek) \"Wed\") (eq (send [%s] get-DayOfWeek) \"Thu\") (eq (send [%s] get-DayOfWeek) \"Fri\"))" +#define CONDITION_WEEKEND "(or (eq (send [%s] get-DayOfWeek) \"Sat\") (eq (send [%s] get-DayOfWeek) \"Sun\"))" + +static std::string generate_initial_fact(ctx::json event_template, ctx::json option); +static std::string generate_event_data(ctx::json event); +static std::string generate_condition_data(std::string rule_id, ctx::json conditions, std::string rule_op, ctx::json* inst_names); + +static std::string int_to_string(int i) +{ + std::ostringstream convert; + convert << i; + std::string str = convert.str(); + return str; +} + +static std::string convert_condition_weekday_weekend(std::string inst_name, std::string day) +{ + std::string buf = (day.compare(TIMER_WEEKDAY) == 0)? CONDITION_WEEKDAY : CONDITION_WEEKEND; + + size_t pos = 0; + while((pos = buf.find("%s", pos)) != std::string::npos) { + buf.replace(pos, 2, inst_name); + pos += inst_name.length(); + } + + return buf; +} + +std::string ctx::script_generator::generate_deftemplate(ctx::json* item) +{ + std::string script; + + std::string name; + item->get(NULL, "name", &name); + + std::set slot; + + std::string opt_name; + for (int i = 0; item->get_array_elem(NULL, "option", i, &opt_name); i++) { + slot.insert(opt_name); + } + + std::string attr_name; + for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) { + slot.insert(attr_name); + } + + //template name is "itemname" + script = "(deftemplate "; + script += name; + script += " "; + + for (std::set::iterator it = slot.begin(); it != slot.end(); ++it){ + script += "(slot "; + script += *it; + script += " (default null))"; + } + script += ")"; + +// _D("Deftemplate script is generated: %s", script.c_str()); + script += "\n"; + + return script; +} + +std::string ctx::script_generator::generate_defclass(ctx::json* item) +{ + std::string script; + + std::string name; + item->get(NULL, "name", &name); + + //class name is "C.itemname" + script = "(defclass C."; + script += name; + script += " (is-a USER) (role concrete) "; + + std::string attr_name; + for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) { + script += "(slot "; + script += attr_name; + script += " (create-accessor read-write))"; + } + script += ")"; + +// _D("Defclass script is generated: %s", script.c_str()); + script += "\n"; + + return script; +} + +std::string ctx::script_generator::generate_makeinstance(ctx::json* item) +{ + std::string script; + + std::string name; + item->get(NULL, "name", &name); + + std::string instance_name; + if (!item->get(NULL, "instance_name", &instance_name)) { + instance_name = name; + } + + //instance name is "[itemname]" + script = "(["; + script += instance_name; + script += "] of C."; + script += name; + + std::string attr_name; + for (int i = 0; item->get_array_elem(NULL, "attributes", i, &attr_name); i++) { + script += " ("; + script += attr_name; + script += " 0)"; + } + script += ")"; + +// _D("Makeinstance script is generated: %s", script.c_str()); + script += "\n"; + + return script; +} + +std::string ctx::script_generator::generate_undefrule(std::string rule_id) +{ + std::string script; + script = "(undefrule rule"; + script += rule_id; + script += ")"; + + return script; +} + +std::string ctx::script_generator::generate_defrule(std::string rule_id, ctx::json event_template, ctx::json rule, ctx::json* inst_names, std::string zone) +{ + std::string script; + ctx::json option = NULL; + rule.get(CT_RULE_EVENT, CT_RULE_EVENT_OPTION, &option); + + script = "(defrule rule"; + script += rule_id; + script += " "; + + script += generate_initial_fact(event_template, option); + script += " => "; + + int eventdata_num = rule.array_get_size(CT_RULE_EVENT, CT_RULE_DATA_ARR); + int condition_num = rule.array_get_size(NULL, CT_RULE_CONDITION); + + if (condition_num > 0) { + // case1: condition exists + script += "(if (and (eq ?*zone* \""; + script += zone; + script += "\") "; + + if (eventdata_num > 0) { + ctx::json event; + rule.get(NULL, CT_RULE_EVENT, &event); + script += generate_event_data(event); + } + + // condition + ctx::json conditions; + rule.get(NULL, CT_RULE_CONDITION, &conditions); + std::string rule_op; + rule.get(NULL, CT_RULE_OPERATOR, &rule_op); + script += generate_condition_data(rule_id, conditions, rule_op, inst_names); + + script += ")"; + script += " then "; + script += " (execute_action rule"; + script += rule_id; + script += "))"; + } else if (eventdata_num > 0) { + // case2: no conditions, but event data + ctx::json event; + rule.get(NULL, CT_RULE_EVENT, &event); + + script += "(if (and (eq ?*zone* \""; + script += zone; + script += "\") "; + script += generate_event_data(event); + script += ") then "; + script += " (execute_action rule"; + script += rule_id; + script += "))"; + } else { + // case3: only action + script += "if (eq ?*zone* \""; + script += zone; + script += "\") then (execute_action rule"; + script += rule_id; + script += ")"; + } + + script += ")"; + _D("Defrule script generated: %s", script.c_str()); + return script; +} + +std::string generate_initial_fact(ctx::json event_template, ctx::json option) +{ + std::string script = "("; + std::string e_name; + event_template.get(NULL, "name", &e_name); + + script += e_name; + script += " "; + + // options + std::string opt_key; + for (int i = 0; event_template.get_array_elem(NULL, "option", i, &opt_key); i++) { + script += "("; + script += opt_key; + script += " "; + + std::string val_str; + int val; + if (option.get(NULL, opt_key.c_str(), &val_str)) { + script += val_str; + } else if (option.get(NULL, opt_key.c_str(), &val)) { + script += int_to_string(val); + } else { + script += "?"; + script += opt_key; + } + + script += ")"; + } + + // attributes + std::string attr; + for (int i = 0; event_template.get_array_elem(NULL, "attributes", i, &attr); i++) { + script += "("; + script += attr; + script += " ?"; + script += attr; + script += ")"; + } + script += ")"; + +// _D("Initial event fact is %s", script.c_str()); + return script; +} + +std::string generate_event_data(ctx::json event) +{ + std::string ename; + event.get(NULL, CT_RULE_EVENT_ITEM, &ename); + + std::string script; + int edata_count = event.array_get_size(NULL, CT_RULE_DATA_ARR); + + if (edata_count > 1) { + std::string item_op; + event.get(NULL, CT_RULE_EVENT_OPERATOR, &item_op); + script += "("; + script += item_op; + script += " "; + } + + ctx::json edata; + for (int i = 0; event.get_array_elem(NULL, CT_RULE_DATA_ARR, i, &edata); i++) { + std::string key_name; + if (ename.compare(TIMER_EVENT_SUBJECT) == 0) { + edata.get(NULL, CT_RULE_DATA_KEY, &key_name); + } + + int valuecount = edata.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR); + int opcount = edata.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR); + std::string key; + edata.get(NULL, CT_RULE_DATA_KEY, &key); + + if (valuecount != opcount) { + _E("Invalid event data. (Value count:%d, Operator count: %d)", valuecount, opcount); + return ""; + } + + if (valuecount > 1) { + script += "("; + + std::string key_op; + edata.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &key_op); + script += key_op; + script += " "; + } + + for (int j = 0; j < valuecount; j++){ + std::string val_op; + std::string val; + edata.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, j, &val_op); + edata.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, j, &val); + + if (key_name.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) { + if (val.compare("\"" TIMER_WEEKDAY "\"") == 0) { + script += (val_op.compare("eq") == 0)? EVENT_WEEKDAY : EVENT_WEEKEND; + continue; + } else if (val.compare("\"" TIMER_WEEKEND "\"") == 0) { + script += (val_op.compare("eq") == 0)? EVENT_WEEKEND : EVENT_WEEKDAY; + continue; + } + } + + script += "("; + script += val_op; + script += " ?"; + script += key; + script += " "; + script += val; + script += ")"; + } + + if (valuecount > 1) { + script += ")"; + } + } + + if (edata_count > 1) { + script += ")"; + } + + return script; +} + +std::string generate_condition_data(std::string rule_id, ctx::json conditions, std::string rule_op, ctx::json* inst_names) +{ + std::string script; + + ctx::json conds; + conds.set(NULL, CT_RULE_CONDITION, conditions); + + int conds_count = conds.array_get_size(NULL, CT_RULE_CONDITION); + if (conds_count > 1) { + script += "("; + script += rule_op; // operator between each condition item + script += " "; + } + + ctx::json it; + for (int i = 0; conds.get_array_elem(NULL, CT_RULE_CONDITION, i, &it); i++) { + std::string cond_name; + it.get(NULL, CT_RULE_CONDITION_ITEM, &cond_name); + + std::string inst_name; + (inst_names)->get(NULL, cond_name.c_str(), &inst_name); + + int data_count = it.array_get_size(NULL, CT_RULE_DATA_ARR); + + if (data_count > 1) { + std::string item_op; + it.get(NULL, CT_RULE_CONDITION_OPERATOR, &item_op); + script += "("; + script += item_op; // operator between data keys of a condition item + script += " "; + } + + ctx::json data; + for (int j = 0; it.get_array_elem(NULL, CT_RULE_DATA_ARR, j, &data); j++) { + std::string key_name; + data.get(NULL, CT_RULE_DATA_KEY, &key_name); + int dval_count = data.array_get_size(NULL, CT_RULE_DATA_VALUE_ARR); + int dvalop_count = data.array_get_size(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR); + + if (dval_count != dvalop_count) { + _E("Invalid condition data. (Data value count %d, Data value operator count %d)", dval_count, dvalop_count); + } + + if (dval_count > 1) { + std::string dkey_op; + data.get(NULL, CT_RULE_DATA_KEY_OPERATOR, &dkey_op); + + script += "("; + script += dkey_op; // operator between comparison data values of a condition data key + script += " " ; + } + + for (int k = 0; k < dval_count; k++) { + std::string dval_op; + std::string dval; + data.get_array_elem(NULL, CT_RULE_DATA_VALUE_OPERATOR_ARR, k, &dval_op); + data.get_array_elem(NULL, CT_RULE_DATA_VALUE_ARR, k, &dval); + + if (inst_name.compare(TIMER_CONDITION_SUBJECT) == 0 && key_name.compare(TIMER_RESPONSE_KEY_DAY_OF_WEEK) == 0) { + if (dval.compare("\"" TIMER_WEEKDAY "\"") == 0) { + script += convert_condition_weekday_weekend(inst_name, (dval_op.compare("eq") == 0)? TIMER_WEEKDAY : TIMER_WEEKEND); + continue; + } else if (dval.compare("\"" TIMER_WEEKEND "\"") == 0) { + script += convert_condition_weekday_weekend(inst_name, (dval_op.compare("eq") == 0)? TIMER_WEEKEND : TIMER_WEEKDAY); + continue; + } + } + + script += "("; + script += dval_op; + script += " (send ["; + script += inst_name; + script += "] get-"; + script += key_name; + script += ") "; + script += dval; + script += ")"; + } + + if (dval_count > 1) { + script += ")"; + } + } + + if (data_count > 1 ) { + script += ")"; + } + } + + if (conds_count > 1) { + script += ")"; + } + + return script; +} + +std::string ctx::script_generator::generate_fact(std::string item_name, ctx::json event_template, ctx::json option, ctx::json data) +{ + // Generate Fact script for invoked event + std::string script = "("; + script += item_name; + script += " "; + + std::string opt_key; + std::string key; + std::string val_str; + int value; + + for (int i = 0; event_template.get_array_elem(NULL, "option", i, &opt_key); i++) { + script += "("; + script += opt_key; + script += " "; + if (option.get(NULL, opt_key.c_str(), &val_str)) { // string type data + script += val_str; + } else if (option.get(NULL, opt_key.c_str(), &value)) { // integer type data + script += int_to_string(value); + } else { + script += "nil"; + } + script += ")"; + } + + for (int i = 0; event_template.get_array_elem(NULL, "attributes", i, &key); i++) { + script += "("; + script += key; + script += " "; + if (data.get(NULL, key.c_str(), &val_str)) { // string type data + script += "\"" + val_str + "\""; + } else if (data.get(NULL, key.c_str(), &value)) { // integer type data + script += int_to_string(value); + } else { + script += "nil"; + } + script += ")"; + } + script += ")"; + + return script; +} + +std::string ctx::script_generator::generate_modifyinstance(std::string instance_name, ctx::json condition_template, ctx::json data) +{ + std::string script = "(modify-instance ["; + script += instance_name; + script += "] "; + + std::string key; + std::string val_str; + int value; + for (int i = 0; condition_template.get_array_elem(NULL, "attributes", i, &key); i++) { + if (data.get(NULL, key.c_str(), &val_str)) { // string type data + script += "(" + key + " \"" + val_str + "\")"; + } else if (data.get(NULL, key.c_str(), &value)) { // integer type data + script += "(" + key + " " + int_to_string(value) + ")"; + } + // else continue; + } + script += ")"; + + return script; +} diff --git a/src/context_trigger/script_generator.h b/src/context_trigger/script_generator.h new file mode 100644 index 0000000..b6fdbb9 --- /dev/null +++ b/src/context_trigger/script_generator.h @@ -0,0 +1,39 @@ +/* + * context-service + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef __CONTEXT_SCRIPT_GENERATOR_H__ +#define __CONTEXT_SCRIPT_GENERATOR_H__ + +namespace ctx { + + namespace script_generator { + + std::string generate_deftemplate(ctx::json* item); + std::string generate_defclass(ctx::json* item); + std::string generate_makeinstance(ctx::json* item); + std::string generate_undefrule(std::string rule_id); + std::string generate_defrule(std::string rule_id, ctx::json event_template, ctx::json rule, ctx::json* inst_names, std::string zone); + std::string generate_fact(std::string item_name, ctx::json event_template, ctx::json option, ctx::json data); + std::string generate_modifyinstance(std::string instance_name, ctx::json condition_template, ctx::json data); + + } + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_SCRIPT_GENERATOR_H__ */ diff --git a/src/context_trigger/timer.cpp b/src/context_trigger/timer.cpp new file mode 100644 index 0000000..76999b6 --- /dev/null +++ b/src/context_trigger/timer.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "timer.h" +#include "trigger.h" +#include "timer_types.h" + +#define MAX_HOUR 24 +#define MAX_DAY 7 + +using namespace ctx::timer_manager; +static GMutex timer_mutex; + +ctx::trigger_timer::ref_count_array_s::ref_count_array_s() +{ + memset(count, 0, sizeof(int) * MAX_DAY); +} + +ctx::trigger_timer::trigger_timer(ctx::context_trigger* tr, std::string z) + : trigger(tr) + , zone(z) +{ +} + +ctx::trigger_timer::~trigger_timer() +{ + clear(); +} + +int ctx::trigger_timer::merge_day_of_week(int* ref_cnt) +{ + int day_of_week = 0; + + for (int d = 0; d < MAX_DAY; ++d) { + if (ref_cnt[d] > 0) { + day_of_week |= (0x01 << d); + } + } + + return day_of_week; +} + +bool ctx::trigger_timer::add(int minute, int day_of_week) +{ + IF_FAIL_RETURN_TAG(minute >=0 && minute < 1440 && + day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY, + false, _E, "Invalid parameter"); + + ctx::scope_mutex sm(&timer_mutex); + + ref_count_array_s &ref = ref_count_map[minute]; + + for (int d = 0; d < MAX_DAY; ++d) { + if ((day_of_week & (0x01 << d)) != 0) { + ref.count[d] += 1; + } + } + + return reset_timer(minute); +} + +bool ctx::trigger_timer::remove(int minute, int day_of_week) +{ + IF_FAIL_RETURN_TAG(minute >=0 && minute < 1440 && + day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY, + false, _E, "Invalid parameter"); + + ctx::scope_mutex sm(&timer_mutex); + + ref_count_array_s &ref = ref_count_map[minute]; + + for (int d = 0; d < MAX_DAY; ++d) { + if ((day_of_week & (0x01 << d)) != 0 && ref.count[d] > 0) { + ref.count[d] -= 1; + } + } + + return reset_timer(minute); +} + +bool ctx::trigger_timer::reset_timer(int minute) +{ + int day_of_week = merge_day_of_week(ref_count_map[minute].count); + timer_state_s &timer = timer_state_map[minute]; + + if (day_of_week == timer.day_of_week) { + /* Necessary timers are already running... */ + return true; + } + + if (day_of_week == 0 && timer.timer_id > 0) { + /* Turn off the timer at hour, if it is not necessray anymore. */ + timer_manager::remove(timer.timer_id); + timer_state_map.erase(minute); + ref_count_map.erase(minute); + return true; + } + + if (timer.timer_id > 0) { + /* Turn off the current timer, to set a new one. */ + timer_manager::remove(timer.timer_id); + timer.timer_id = -1; + timer.day_of_week = 0; + } + + /* Create a new timer, w.r.t. the new day_of_week value. */ + int h = minute / 60; + int m = minute - h * 60; + int tid = timer_manager::set_at(h, m, day_of_week, this); + IF_FAIL_RETURN_TAG(tid > 0, false, _E, "Timer setting failed"); + + timer.timer_id = tid; + timer.day_of_week = day_of_week; + + return true; +} + +void ctx::trigger_timer::clear() +{ + ctx::scope_mutex sm(&timer_mutex); + + for (timer_state_map_t::iterator it = timer_state_map.begin(); it != timer_state_map.end(); ++it) { + if (it->second.timer_id > 0) { + timer_manager::remove(it->second.timer_id); + } + } + + timer_state_map.clear(); + ref_count_map.clear(); +} + +bool ctx::trigger_timer::on_timer_expired(int timer_id, void* user_data) +{ + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + tzset(); + localtime_r(&rawtime, &timeinfo); + + int hour = timeinfo.tm_hour; + int min = timeinfo.tm_min; + int day_of_week = (0x01 << timeinfo.tm_wday); + + on_timer_expired(hour, min, day_of_week); + + return true; +} + +void ctx::trigger_timer::on_timer_expired(int hour, int min, int day_of_week) +{ + _I("[Timer-%s] Time: %02d:%02d, Day of Week: %#x", zone.c_str(), hour, min, day_of_week); + + ctx::json result; + result.set(NULL, TIMER_RESPONSE_KEY_TIME_OF_DAY, hour * 60 + min); + result.set(NULL, TIMER_RESPONSE_KEY_DAY_OF_WEEK, convert_day_of_week_to_string(day_of_week)); + + ctx::json dummy = NULL; + trigger->push_fact(TIMER_EVENT_REQ_ID, ERR_NONE, TIMER_EVENT_SUBJECT, dummy, result, zone.c_str()); +} + +int ctx::trigger_timer::get_day_of_month() +{ + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + tzset(); + localtime_r(&rawtime, &timeinfo); + + int day_of_month = timeinfo.tm_mday; + + return day_of_month; +} + +std::string ctx::trigger_timer::get_day_of_week() +{ + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + tzset(); + localtime_r(&rawtime, &timeinfo); + + int day_of_week = (0x01 << timeinfo.tm_wday); + + return convert_day_of_week_to_string(day_of_week); +} + +int ctx::trigger_timer::get_minute_of_day() +{ + time_t rawtime; + struct tm timeinfo; + + time(&rawtime); + tzset(); + localtime_r(&rawtime, &timeinfo); + + int hour = timeinfo.tm_hour; + int minute = timeinfo.tm_min; + + return hour * 60 + minute; +} + +int ctx::trigger_timer::convert_string_to_day_of_week(std::string d) +{ + int day = 0; + d = d.substr(1, d.length() - 2); + + if (d.compare(TIMER_SUN) == 0) { + day = SUN; + } else if (d.compare(TIMER_MON) == 0) { + day = MON; + } else if (d.compare(TIMER_TUE) == 0) { + day = TUE; + } else if (d.compare(TIMER_WED) == 0) { + day = WED; + } else if (d.compare(TIMER_THU) == 0) { + day = THU; + } else if (d.compare(TIMER_FRI) == 0) { + day = FRI; + } else if (d.compare(TIMER_SAT) == 0) { + day = SAT; + } else if (d.compare(TIMER_WEEKDAY) == 0) { + day = WEEKDAY; + } else if (d.compare(TIMER_WEEKEND) == 0) { + day = WEEKEND; + } else if (d.compare(TIMER_EVERYDAY) == 0) { + day = EVERYDAY; + } + + return day; +} + +std::string ctx::trigger_timer::convert_day_of_week_to_string(int d) +{ + std::string day; + + if (d == SUN) { + day = TIMER_SUN; + } else if (d == MON) { + day = TIMER_MON; + } else if (d == TUE) { + day = TIMER_TUE; + } else if (d == WED) { + day = TIMER_WED; + } else if (d == THU) { + day = TIMER_THU; + } else if (d == FRI) { + day = TIMER_FRI; + } else if (d == SAT) { + day = TIMER_SAT; + } else if (d == WEEKDAY) { + day = TIMER_WEEKDAY; + } else if (d == WEEKEND) { + day = TIMER_WEEKEND; + } else if (d == EVERYDAY) { + day = TIMER_EVERYDAY; + } + + return day; +} + +bool ctx::trigger_timer::empty() +{ + return timer_state_map.empty(); +} diff --git a/src/context_trigger/timer.h b/src/context_trigger/timer.h new file mode 100644 index 0000000..3235abe --- /dev/null +++ b/src/context_trigger/timer.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_CONTEXT_TRIGGER_TIMER_H__ +#define __CONTEXT_CONTEXT_TRIGGER_TIMER_H__ + +#include +#include +#include +#include + +namespace ctx { + + class context_trigger; + + class trigger_timer : public timer_listener_iface { + private: + struct ref_count_array_s { + int count[7]; /* reference counts for days of week*/ + ref_count_array_s(); + }; + + struct timer_state_s { + int timer_id; + int day_of_week; /* day of week, merged into one integer */ + timer_state_s() : timer_id(-1), day_of_week(0) {} + }; + + typedef std::map ref_count_map_t; + typedef std::map timer_state_map_t; + + ctx::context_trigger *trigger; + std::string zone; + ref_count_map_t ref_count_map; + timer_state_map_t timer_state_map; + + int merge_day_of_week(int *ref_cnt); + bool reset_timer(int hour); + void on_timer_expired(int hour, int min, int day_of_week); + + protected: + bool on_timer_expired(int timer_id, void *user_data); + + public: + trigger_timer(ctx::context_trigger *tr, std::string z); + ~trigger_timer(); + + bool add(int minute, int day_of_week); + bool remove(int minute, int day_of_week); + void clear(); + static int get_day_of_month(); + static std::string get_day_of_week(); + static int get_minute_of_day(); + static int convert_string_to_day_of_week(std::string d); + static std::string convert_day_of_week_to_string(int d); + bool empty(); + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_CONTEXT_TRIGGER_TIMER_H__ */ diff --git a/src/context_trigger/timer_types.h b/src/context_trigger/timer_types.h new file mode 100644 index 0000000..250858a --- /dev/null +++ b/src/context_trigger/timer_types.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_TIMER_TYPES_H__ +#define __CONTEXT_TIMER_TYPES_H__ + +#define TIMER_EVENT_SUBJECT "timer/event" +#define TIMER_CONDITION_SUBJECT "timer/state" + +#define TIMER_RESPONSE_KEY_TIME_OF_DAY "TimeOfDay" +#define TIMER_RESPONSE_KEY_DAY_OF_WEEK "DayOfWeek" +#define TIMER_RESPONSE_KEY_DAY_OF_MONTH "DayOfMonth" + +#define TIMER_EVENT_REQ_ID -1 + +#define TIMER_MON "Mon" +#define TIMER_TUE "Tue" +#define TIMER_WED "Wed" +#define TIMER_THU "Thu" +#define TIMER_FRI "Fri" +#define TIMER_SAT "Sat" +#define TIMER_SUN "Sun" +#define TIMER_WEEKDAY "Weekday" +#define TIMER_WEEKEND "Weekend" +#define TIMER_EVERYDAY "Everyday" + +#endif //__CONTEXT_TIMER_TYPES_H__ diff --git a/src/context_trigger/trigger.cpp b/src/context_trigger/trigger.cpp new file mode 100644 index 0000000..abde829 --- /dev/null +++ b/src/context_trigger/trigger.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "fact_reader.h" +#include "trigger.h" +#include "rule_manager.h" + +ctx::context_trigger::context_trigger() + : _reader(NULL) +{ +} + +ctx::context_trigger::~context_trigger() +{ +} + +bool ctx::context_trigger::init(ctx::context_manager_impl* mgr) +{ + // Do the necessary initialization process. + // This function is called from the main thread during the service launching process. + _reader = new(std::nothrow) fact_reader(mgr, this); + IF_FAIL_RETURN_TAG(_reader, false, _E, "Memory allocation failed"); + + _D("Starting Context Trigger Thread"); + IF_FAIL_RETURN(start(), false); + + push_thread_event(ETYPE_INITIALIZE, NULL); + + return true; +} + +void ctx::context_trigger::release() +{ + _D("Stopping Context Trigger Thread"); + stop(); + + // Release the occupied resources. + // This function is called from the main thread during the service termination process. + delete _reader; + _reader = NULL; +} + +void ctx::context_trigger::on_thread_event_popped(int type, void* data) +{ + switch (type) { + case ETYPE_REQUEST: + IF_FAIL_VOID(data); + process_request(static_cast(data)); + break; + case ETYPE_FACT: + IF_FAIL_VOID(data); + process_fact(static_cast(data)); + break; + case ETYPE_INITIALIZE: + process_initialize(); + break; + default: + _W("Unknown event type"); + return; + } + + delete_thread_event(type, data); +} + +void ctx::context_trigger::delete_thread_event(int type, void* data) +{ + IF_FAIL_VOID(data); + + switch (type) { + case ETYPE_REQUEST: + { + std::string subject = static_cast(data)->get_subject(); + if (subject != CONTEXT_TRIGGER_SUBJECT_ENABLE) { + delete (static_cast(data)); + } + } + return; + case ETYPE_FACT: + delete (static_cast(data)); + return; + case ETYPE_INITIALIZE: + return; + default: + _W("Unknown event type"); + return; + } +} + +bool ctx::context_trigger::assign_request(ctx::request_info* request) +{ + std::string subject = request->get_subject(); + if (subject != CONTEXT_TRIGGER_SUBJECT_ADD && subject != CONTEXT_TRIGGER_SUBJECT_REMOVE && + subject != CONTEXT_TRIGGER_SUBJECT_ENABLE && subject != CONTEXT_TRIGGER_SUBJECT_DISABLE && + subject != CONTEXT_TRIGGER_SUBJECT_GET && subject != CONTEXT_TRIGGER_SUBJECT_GET_RULE_IDS) { + return false; + } + + push_thread_event(ETYPE_REQUEST, request); + return true; +} + +void ctx::context_trigger::process_request(ctx::request_info* request) +{ + // Process the request, and reply to the client if necessary. + const char* req_sbj = request->get_subject(); + _D("Request is %s", req_sbj); + std::string subject(req_sbj); + + if (subject == CONTEXT_TRIGGER_SUBJECT_ADD) { + add_rule(request); + } else if (subject == CONTEXT_TRIGGER_SUBJECT_REMOVE) { + remove_rule(request); + } else if (subject == CONTEXT_TRIGGER_SUBJECT_ENABLE) { + enable_rule(request); + } else if (subject == CONTEXT_TRIGGER_SUBJECT_DISABLE) { + disable_rule(request); + } else if (subject == CONTEXT_TRIGGER_SUBJECT_GET) { + get_rule_by_id(request); + } else if (subject == CONTEXT_TRIGGER_SUBJECT_GET_RULE_IDS) { + get_rule_ids(request); + } else { + _E("Invalid request"); + } +} + +void ctx::context_trigger::push_fact(int req_id, int error, const char* subject, ctx::json& option, ctx::json& data, const char* zone) +{ + context_fact *fact = new(std::nothrow) context_fact(req_id, error, subject, option, data, zone); + IF_FAIL_VOID_TAG(fact, _E, "Memory allocation failed"); + + push_thread_event(ETYPE_FACT, fact); +} + +void ctx::context_trigger::process_fact(ctx::context_fact* fact) +{ + // Process the context fact. + rule_mgr->on_event_received(fact->get_subject(), fact->get_option(), fact->get_data(), fact->get_zone_name()); +} + +void ctx::context_trigger::process_initialize(void) +{ + rule_mgr = new(std::nothrow) rule_manager(); + IF_FAIL_VOID_TAG(rule_mgr, _E, "Memory allocation failed"); + + bool ret = rule_mgr->init(this, _reader); + if (!ret) { + _E("Context trigger initialization failed."); + raise(SIGTERM); + } +} + +void ctx::context_trigger::add_rule(ctx::request_info* request) +{ + ctx::json rule_id; + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + const char* zone_name = request->get_zone_name(); + if (zone_name == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + int error = rule_mgr->add_rule(app_id, request->get_description(), zone_name, &rule_id); + _I("'%s' adds a rule (Error: %#x)", request->get_client(), error); + + request->reply(error, rule_id); +} + +void ctx::context_trigger::remove_rule(ctx::request_info* request) +{ + int id; + int error; + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + ctx::json rule_id = request->get_description(); + rule_id.get(NULL, CT_RULE_ID, &id); + + error = rule_mgr->check_rule(app_id, id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = rule_mgr->is_rule_enabled(id); + if (ret) { + request->reply(ERR_RULE_ENABLED); + return; + } + + error = rule_mgr->remove_rule(id); + _I("'%s' removes rule%d (Error: %#x)", request->get_client(), id, error); + request->reply(error); +} + +void ctx::context_trigger::enable_rule(ctx::request_info* request) +{ + int id; + int error; + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + ctx::json rule_id = request->get_description(); + rule_id.get(NULL, CT_RULE_ID, &id); + + error = rule_mgr->check_rule(app_id, id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = rule_mgr->is_rule_enabled(id); + if (ret) { + request->reply(ERR_RULE_ENABLED); + return; + } + + error = rule_mgr->enable_rule(id); + _I("'%s' enables rule%d (Error: %#x)", request->get_client(), id, error); + request->reply(error); +} + +void ctx::context_trigger::disable_rule(ctx::request_info* request) +{ + int id; + int error; + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + ctx::json rule_id = request->get_description(); + rule_id.get(NULL, CT_RULE_ID, &id); + + error = rule_mgr->check_rule(app_id, id); + if (error != ERR_NONE) { + request->reply(error); + return; + } + + bool ret = rule_mgr->is_rule_enabled(id); + if (!ret) { + request->reply(ERR_RULE_NOT_ENABLED); + return; + } + + error = rule_mgr->disable_rule(id); + _I("'%s' disables rule%d (Error: %#x)", request->get_client(), id, error); + request->reply(error); +} + +void ctx::context_trigger::get_rule_by_id(ctx::request_info* request) +{ + int error; + + ctx::json option = request->get_description(); + int id; + option.get(NULL, CT_RULE_ID, &id); + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + ctx::json read_data; + error = rule_mgr->get_rule_by_id(app_id, id, &read_data); + + ctx::json dummy; + request->reply(error, dummy, read_data); +} + +void ctx::context_trigger::get_rule_ids(ctx::request_info* request) +{ + int error; + + const char* app_id = request->get_app_id(); + if (app_id == NULL) { + request->reply(ERR_OPERATION_FAILED); + return; + } + + ctx::json read_data; + error = rule_mgr->get_rule_ids(app_id, &read_data); + + ctx::json dummy; + request->reply(error, dummy, read_data); +} diff --git a/src/context_trigger/trigger.h b/src/context_trigger/trigger.h new file mode 100644 index 0000000..eda318a --- /dev/null +++ b/src/context_trigger/trigger.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_CONTEXT_TRIGGER_H__ +#define __CONTEXT_CONTEXT_TRIGGER_H__ + +#include +#include "../request.h" +#include "fact.h" + +namespace ctx { + + class fact_reader; + class rule_manager; + class client_request; + class context_manager_impl; + class context_trigger : public event_driven_thread { + public: + context_trigger(); + ~context_trigger(); + + bool init(ctx::context_manager_impl* mgr); + void release(); + + bool assign_request(ctx::request_info* request); + void push_fact(int req_id, int error, const char* subject, ctx::json& option, ctx::json& data, const char* zone); + bool publish(int rule_id, int error, ctx::json data); + bool _publish(int rule_id, int error, ctx::json data); + + + private: + enum event_type_e { + ETYPE_REQUEST = 1, // A request received from a client + ETYPE_FACT, // A context fact received from a CA + ETYPE_INITIALIZE, // Initialization + }; + + ctx::fact_reader *_reader; + + void on_thread_event_popped(int type, void* data); + void delete_thread_event(int type, void* data); + + void process_request(ctx::request_info* request); + void process_fact(ctx::context_fact* fact); + void process_initialize(void); + + void add_rule(ctx::request_info* request); + void remove_rule(ctx::request_info* request); + void enable_rule(ctx::request_info* request); + void disable_rule(ctx::request_info* request); + void get_rule_by_id(ctx::request_info* request); + void get_rule_ids(ctx::request_info* request); + + ctx::rule_manager* rule_mgr; + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_CONTEXT_TRIGGER_H__ */ diff --git a/src/db_mgr_impl.cpp b/src/db_mgr_impl.cpp new file mode 100644 index 0000000..9facb0e --- /dev/null +++ b/src/db_mgr_impl.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "server.h" +#include "db_mgr_impl.h" + +//TODO: conf file for this kind of env. params. +#define CONTEXT_DB_PATH "/opt/dbspace/.context-service.db" + +static bool initialized = false; +static GThread *mainthread = NULL; +static GMutex sync_insert_mutex; +static GMutex sync_execute_mutex; +static GCond sync_insert_cond; +static GCond sync_execute_cond; +static int64_t sync_insert_row_id = -1; +static unsigned int sync_insert_qid = 0; +static int sync_execute_error; +static std::vector sync_execute_result; +static unsigned int sync_execute_qid = 0; + +ctx::db_manager_impl::db_manager_impl() + : db_handle(NULL) +{ +} + +ctx::db_manager_impl::~db_manager_impl() +{ + release(); +} + +bool ctx::db_manager_impl::init() +{ + IF_FAIL_RETURN_TAG(!initialized, true, _W, "Re-initialization"); + IF_FAIL_RETURN(open(), false); + mainthread = g_thread_self(); + IF_FAIL_RETURN(start(), false); + initialized = true; + return true; +} + +void ctx::db_manager_impl::release() +{ + IF_FAIL_VOID(initialized); + initialized = false; + stop(); + close(); +} + +bool ctx::db_manager_impl::open() +{ + sqlite3 *db = NULL; + int r = sqlite3_open(CONTEXT_DB_PATH, &db); + + IF_FAIL_RETURN_TAG(r == SQLITE_OK, false, _E, "DB Error: %s", sqlite3_errmsg(db)); + + db_handle = db; + return true; +} + +void ctx::db_manager_impl::close() +{ + if (db_handle) { + sqlite3_close(db_handle); + } + db_handle = NULL; +} + +bool ctx::db_manager_impl::is_main_thread() +{ + return mainthread == g_thread_self(); +} + +void ctx::db_manager_impl::on_thread_event_popped(int type, void* data) +{ + IF_FAIL_VOID(data); + query_info_s *info = static_cast(data); + + switch (type) { + case QTYPE_CREATE_TABLE: + case QTYPE_INSERT: + case QTYPE_EXECUTE: + _execute(type, info->id, info->query.c_str(), info->listener); + break; + default: + _W("Unknown type: %d", type); + break; + } + + delete_thread_event(type, data); +} + +void ctx::db_manager_impl::delete_thread_event(int type, void* data) +{ + IF_FAIL_VOID(data); + query_info_s *info = static_cast(data); + delete info; +} + +bool ctx::db_manager_impl::create_table(unsigned int query_id, const char* table_name, const char* columns, const char* option, db_listener_iface* listener) +{ + IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized"); + + query_info_s *info = new(std::nothrow) query_info_s; + IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed"); + + info->query = "CREATE TABLE IF NOT EXISTS "; + info->query = info->query + table_name + " (row_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + columns + ")"; + if (option) { + info->query = info->query + " " + option; + } + info->query += ";"; + + info->id = query_id; + info->listener = listener; + + if (!push_thread_event(QTYPE_CREATE_TABLE, info)) { + _E("Pushing thread event failed"); + delete info; + return false; + } + + return true; +} + +bool ctx::db_manager_impl::insert(unsigned int query_id, const char* table_name, json& record, db_listener_iface* listener) +{ + IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized"); + + std::list keys; + IF_FAIL_RETURN_TAG(record.get_keys(&keys), false, _E, "Invalid record"); + + std::ostringstream colstream; + std::ostringstream valstream; + + + for (std::list::iterator it = keys.begin(); it != keys.end(); ++it) { + std::string s; + int64_t i; + if (record.get(NULL, (*it).c_str(), &s)) { + colstream << *it << ","; + valstream << "'" << s << "',"; + } else if (record.get(NULL, (*it).c_str(), &i)) { + colstream << *it << ","; + valstream << i << ","; + } + } + + std::string cols = colstream.str(); + std::string vals = valstream.str(); + + IF_FAIL_RETURN_TAG(!cols.empty(), false, _E, "Invalid record"); + + cols.erase(cols.size() - 1); + vals.erase(vals.size() - 1); + + _D("Columns: %s", cols.c_str()); + _D("Values : %s", vals.c_str()); + + query_info_s *info = new(std::nothrow) query_info_s; + IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed"); + + info->id = query_id; + info->listener = listener; + + info->query = "INSERT INTO "; + info->query = info->query + table_name + " (" + cols + ") VALUES (" + vals + ");"; + info->query = info->query + "SELECT seq FROM sqlite_sequence WHERE name='" + table_name + "';"; + + if (!push_thread_event(QTYPE_INSERT, info)) { + _E("Pushing thread event failed"); + delete info; + return false; + } + + return true; +} + +bool ctx::db_manager_impl::execute(unsigned int query_id, const char* query, db_listener_iface* listener) +{ + IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized"); + + query_info_s *info = new(std::nothrow) query_info_s; + IF_FAIL_RETURN_TAG(info, false, _E, "Memory allocation failed"); + + info->id = query_id; + info->query = query; + info->listener = listener; + + if (!push_thread_event(QTYPE_EXECUTE, info)) { + _E("Pushing thread event failed"); + delete info; + return false; + } + + return true; +} + +void ctx::db_manager_impl::_execute(int query_type, unsigned int query_id, const char* query, db_listener_iface* listener) +{ + _SD("SQL(%d): %s", query_id, query); + IF_FAIL_VOID(query); + IF_FAIL_VOID_TAG(db_handle, _E, "DB not opened"); + + std::vector *query_result = new(std::nothrow) std::vector; + IF_FAIL_VOID_TAG(query_result, _E, "Memory allocation failed"); + + char *err = NULL; + + int ret = sqlite3_exec(db_handle, query, execution_result_cb, query_result, &err); + if (ret != SQLITE_OK) { + _E("DB Error: %s", err); + sqlite3_free(err); + send_result(query_type, query_id, listener, ERR_OPERATION_FAILED, query_result); + return; + } + + send_result(query_type, query_id, listener, ERR_NONE, query_result); + return; +} + +int ctx::db_manager_impl::execution_result_cb(void *user_data, int dim, char **value, char **column) +{ + std::vector *records = static_cast*>(user_data); + json row; + bool column_null = false; + + for (int i=0; ipush_back(row); + } else { + _W(RED("Null columns exist")); + } + + return 0; +} + +void ctx::db_manager_impl::send_result(int query_type, unsigned int query_id, db_listener_iface* listener, int error, std::vector* result) +{ + query_result_s *qr = new(std::nothrow) query_result_s(); + IF_FAIL_VOID_TAG(qr, _E, "Memory allocation failed"); + + qr->type = query_type; + qr->id = query_id; + qr->listener = listener; + qr->error = error; + qr->result = result; + + g_idle_add(_send_result, qr); +} + +gboolean ctx::db_manager_impl::_send_result(gpointer data) +{ + query_result_s *qr = static_cast(data); + + if (qr->listener) { + switch (qr->type) { + case QTYPE_CREATE_TABLE: + qr->listener->on_creation_result_received(qr->id, qr->error); + break; + case QTYPE_INSERT: + { + int64_t row_id = -1; + if (qr->error == ERR_NONE && qr->result && !qr->result->empty()) { + qr->result->at(0).get(NULL, "seq", &row_id); + _D("RowId: %d", row_id); + } + qr->listener->on_insertion_result_received(qr->id, qr->error, row_id); + } + break; + case QTYPE_EXECUTE: + qr->listener->on_query_result_received(qr->id, qr->error, *(qr->result)); + break; + default: + _W("Unknown query type: %d", qr->type); + } + } + + delete qr->result; + delete qr; + return FALSE; +} + +static unsigned int generate_qid() +{ + static GMutex mutex; + static unsigned int qid = 0; + + ctx::scope_mutex sm(&mutex); + + ++qid; + + if (qid == 0) { // Overflow handling + qid = 1; + } + + return qid; +} + +bool ctx::db_manager_impl::insert_sync(const char* table_name, json& record, int64_t* row_id) +{ + IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized"); + IF_FAIL_RETURN_TAG(!db_manager_impl::is_main_thread(), false, _E, "Cannot use this in the main thread"); + IF_FAIL_RETURN_TAG(table_name && row_id, false, _E, "Invalid parameter"); + + ctx::scope_mutex sm(&sync_insert_mutex); + + unsigned int qid = generate_qid(); + insert(qid, table_name, record, this); + + while (sync_insert_qid != qid) { + g_cond_wait(&sync_insert_cond, &sync_insert_mutex); + } + + *row_id = sync_insert_row_id; + + IF_FAIL_RETURN_TAG(*row_id > 0, false, _E, "Insertion failed"); + return true; +} + +void ctx::db_manager_impl::on_insertion_result_received(unsigned int query_id, int error, int64_t row_id) +{ + ctx::scope_mutex sm(&sync_insert_mutex); + + sync_insert_row_id = row_id; + sync_insert_qid = query_id; + + g_cond_signal(&sync_insert_cond); +} + +bool ctx::db_manager_impl::execute_sync(const char* query, std::vector* records) +{ + IF_FAIL_RETURN_TAG(initialized, false, _E, "Not initialized"); + IF_FAIL_RETURN_TAG(!db_manager_impl::is_main_thread(), false, _E, "Cannot use this in the main thread"); + IF_FAIL_RETURN_TAG(query && records, false, _E, "Invalid parameter"); + + ctx::scope_mutex sm(&sync_execute_mutex); + + unsigned int qid = generate_qid(); + execute(qid, query, this); + + while (sync_execute_qid != qid) { + g_cond_wait(&sync_execute_cond, &sync_execute_mutex); + } + + IF_FAIL_RETURN_TAG(sync_execute_error == ERR_NONE, false, _E, "Query execution failed"); + + *records = sync_execute_result; + sync_execute_result.clear(); + + return true; +} + +void ctx::db_manager_impl::on_query_result_received(unsigned int query_id, int error, std::vector& records) +{ + ctx::scope_mutex sm(&sync_execute_mutex); + + sync_execute_error = error; + sync_execute_result = records; + sync_execute_qid = query_id; + + g_cond_signal(&sync_execute_cond); +} + +void ctx::db_manager_impl::on_creation_result_received(unsigned int query_id, int error) +{ + _D("Table Created: QID: %d, Error: %#x", query_id, error); +} diff --git a/src/db_mgr_impl.h b/src/db_mgr_impl.h new file mode 100644 index 0000000..97baaa0 --- /dev/null +++ b/src/db_mgr_impl.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_DB_MANAGER_IMPL_H__ +#define __CONTEXT_DB_MANAGER_IMPL_H__ + +#include +#include +#include + +#include +#include +#include + +namespace ctx { + + class db_manager_impl : public event_driven_thread, public db_listener_iface, public db_manager_iface { + private: + enum query_type_e { + QTYPE_CREATE_TABLE = 1, + QTYPE_INSERT, + QTYPE_EXECUTE, + }; + + struct query_info_s { + unsigned int id; + db_listener_iface* listener; + std::string query; + }; + + struct query_result_s { + int type; + unsigned int id; + int error; + db_listener_iface* listener; + std::vector* result; + }; + + sqlite3 *db_handle; + + static bool is_main_thread(); + + void on_thread_event_popped(int type, void* data); + void delete_thread_event(int type, void* data); + + bool open(); + void close(); + + void _execute(int query_type, unsigned int query_id, const char* query, db_listener_iface* listener); + void send_result(int query_type, unsigned int query_id, db_listener_iface* listener, int error, std::vector* result); + + static int execution_result_cb(void *user_data, int dim, char **value, char **column); + static gboolean _send_result(gpointer data); + + void on_creation_result_received(unsigned int query_id, int error); + void on_insertion_result_received(unsigned int query_id, int error, int64_t row_id); + void on_query_result_received(unsigned int query_id, int error, std::vector& records); + + public: + db_manager_impl(); + ~db_manager_impl(); + + bool init(); + void release(); + + bool create_table(unsigned int query_id, const char* table_name, const char* columns, const char* option = NULL, db_listener_iface* listener = NULL); + bool insert(unsigned int query_id, const char* table_name, json& record, db_listener_iface* listener = NULL); + bool execute(unsigned int query_id, const char* query, db_listener_iface* listener); + bool insert_sync(const char* table_name, json& record, int64_t* row_id); + bool execute_sync(const char* query, std::vector* records); + + }; /* class db_manager */ +} + +#endif /* __CONTEXT_DB_MANAGER_IMPL_H__ */ diff --git a/src/dbus_server_impl.cpp b/src/dbus_server_impl.cpp new file mode 100644 index 0000000..1834df7 --- /dev/null +++ b/src/dbus_server_impl.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include "server.h" +#include "client_request.h" +#include "dbus_server_impl.h" + +static ctx::dbus_server_impl *_instance = NULL; +static GDBusConnection *dbus_connection = NULL; +static guint dbus_owner_id = 0; +static GDBusNodeInfo *dbus_node_info = NULL; + +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static const char* req_type_to_str(int req_type) +{ + switch (req_type) { + case REQ_SUBSCRIBE: + return "Subscribe"; + case REQ_UNSUBSCRIBE: + return "Unsubscribe"; + case REQ_READ: + return "Read"; + case REQ_READ_SYNC: + return "Read (Sync)"; + case REQ_WRITE: + return "Write"; + default: + return NULL; + } +} + +static void handle_request(const char *sender, GVariant *param, GDBusMethodInvocation *invocation) +{ + gint req_type = 0; + const gchar *cookie = NULL; + gint req_id = 0; + const gchar *subject = NULL; + const gchar *input = NULL; + + g_variant_get(param, "(i&si&s&s)", &req_type, &cookie, &req_id, &subject, &input); + IF_FAIL_VOID_TAG(req_type > 0 && req_id > 0 && cookie && subject && input, _E, "Invalid request"); + + _SD("Cookie: %s", cookie); + _I("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject); + _SI("Input: %s", input); + + //TODO: Parameter validation + + ctx::client_request *request = NULL; + try { + request = new ctx::client_request(req_type, sender, req_id, subject, input, cookie, invocation); + } catch (std::bad_alloc& ba) { + _E("Memory allocation failed"); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + return; + } catch (int e) { + _E("Caught %d", e); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + return; + } + + ctx::server::send_request(request); +} + +static void handle_method_call(GDBusConnection *conn, const gchar *sender, + const gchar *obj_path, const gchar *iface, const gchar *method_name, + GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data) +{ + IF_FAIL_VOID_TAG(STR_EQ(obj_path, DBUS_PATH), _W, "Invalid path: %s", obj_path); + IF_FAIL_VOID_TAG(STR_EQ(iface, DBUS_IFACE), _W, "Invalid interface: %s", obj_path); + + if (STR_EQ(method_name, METHOD_REQUEST)) { + handle_request(sender, param, invocation); + } else { + _W("Invalid method: %s", method_name); + } +} + +static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data) +{ + GDBusInterfaceVTable vtable; + vtable.method_call = handle_method_call; + vtable.get_property = NULL; + vtable.set_property = NULL; + + guint reg_id = g_dbus_connection_register_object(conn, DBUS_PATH, + dbus_node_info->interfaces[0], &vtable, NULL, NULL, NULL); + + if (reg_id <= 0) { + _E("Failed to acquire dbus"); + raise(SIGTERM); + } + + dbus_connection = conn; +} + +static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data) +{ + _SI("Dbus name acquired: %s", name); +} + +static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data) +{ + _E("Dbus name lost"); + raise(SIGTERM); +} + +ctx::dbus_server_impl::dbus_server_impl() +{ +} + +ctx::dbus_server_impl::~dbus_server_impl() +{ + release(); +} + +bool ctx::dbus_server_impl::init() +{ + IF_FAIL_RETURN_TAG(dbus_node_info == NULL, false, _E, "Re-initialization"); + + dbus_node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + IF_FAIL_RETURN_TAG(dbus_node_info != NULL, false, _E, "Initialization failed"); + + dbus_owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, DBUS_DEST, G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); + + _instance = this; + return true; +} + +void ctx::dbus_server_impl::release() +{ + if (dbus_connection) { + g_dbus_connection_flush_sync(dbus_connection, NULL, NULL); + } + + if (dbus_owner_id > 0) { + g_bus_unown_name(dbus_owner_id); + dbus_owner_id = 0; + } + + if (dbus_connection) { + g_dbus_connection_close_sync(dbus_connection, NULL, NULL); + g_object_unref(dbus_connection); + dbus_connection = NULL; + } + + if (dbus_node_info) { + g_dbus_node_info_unref(dbus_node_info); + dbus_node_info = NULL; + } +} + +void ctx::dbus_server_impl::publish(const char* dest, int req_id, const char* subject, int error, const char* data) +{ + IF_FAIL_VOID_TAG(dest && subject && data, _E, "Parameter null"); + + _SI("Publish: %s, %d, %s, %#x, %s", dest, req_id, subject, error, data); + + GVariant *param = g_variant_new("(isis)", req_id, subject, error, data); + IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed"); + + GError *err = NULL; + g_dbus_connection_call(dbus_connection, dest, DBUS_PATH, DBUS_IFACE, + METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err); + HANDLE_GERROR(err); +} + +static void handle_signal_received(GDBusConnection *conn, const gchar *sender, + const gchar *obj_path, const gchar *iface, const gchar *signal_name, + GVariant *param, gpointer user_data) +{ + IF_FAIL_VOID_TAG(user_data, _W, "user_data cannot be null"); + ctx::dbus_listener_iface *listener = static_cast(user_data); + listener->on_signal_received(sender, obj_path, iface, signal_name, param); +} + +int64_t ctx::dbus_server_impl::signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, ctx::dbus_listener_iface* listener) +{ + IF_FAIL_RETURN_TAG(dbus_connection, -1, _E, "Dbus not connected"); + guint sid = g_dbus_connection_signal_subscribe(dbus_connection, + sender, iface, name, path, NULL, G_DBUS_SIGNAL_FLAGS_NONE, + handle_signal_received, listener, NULL); + return static_cast(sid); +} + +void ctx::dbus_server_impl::signal_unsubscribe(int64_t subscription_id) +{ + IF_FAIL_VOID_TAG(dbus_connection, _E, "Dbus not connected"); + IF_FAIL_VOID_TAG(subscription_id >= 0, _W, "Invalid parameter"); + g_dbus_connection_signal_unsubscribe(dbus_connection, static_cast(subscription_id)); +} + +void ctx::dbus_server::publish(const char* dest, int req_id, const char* subject, int error, const char* data) +{ + _instance->publish(dest, req_id, subject, error, data); +} diff --git a/src/dbus_server_impl.h b/src/dbus_server_impl.h new file mode 100644 index 0000000..4980d1d --- /dev/null +++ b/src/dbus_server_impl.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_DBUS_SERVER_IMPL_H__ +#define __CONTEXT_DBUS_SERVER_IMPL_H__ + +#include +#include +#include + +namespace ctx { + class dbus_server_impl : public dbus_server_iface { + public: + dbus_server_impl(); + ~dbus_server_impl(); + + bool init(); + void release(); + + void publish(const char* dest, int req_id, const char* subject, int error, const char* data); + int64_t signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, dbus_listener_iface* listener); + void signal_unsubscribe(int64_t subscription_id); + + }; /* class ctx::dbus_server */ + + namespace dbus_server { + void publish(const char* dest, int req_id, const char* subject, int error, const char* data); + } +} /* namespace ctx */ + +#endif /* End of __CONTEXT_DBUS_SERVER_IMPL_H__ */ diff --git a/src/request.cpp b/src/request.cpp new file mode 100644 index 0000000..29e3130 --- /dev/null +++ b/src/request.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "request.h" + +ctx::request_info::request_info(int type, const char* client, int req_id, const char* subj, const char* desc) + : _type(type) + , _req_id(req_id) + , _client(client) + , _subject(subj) + , _description(desc) +{ +} + +ctx::request_info::~request_info() +{ +} + +int ctx::request_info::get_type() +{ + return _type; +} + +int ctx::request_info::get_id() +{ + return _req_id; +} + +const char* ctx::request_info::get_zone_name() +{ + return _zone_name.c_str(); +} + +const char* ctx::request_info::get_client() +{ + return _client.c_str(); +} + +const char* ctx::request_info::get_subject() +{ + return _subject.c_str(); +} + +const char* ctx::request_info::get_app_id() +{ + return get_client(); +} + +ctx::json& ctx::request_info::get_description() +{ + return _description; +} diff --git a/src/request.h b/src/request.h new file mode 100644 index 0000000..bf8902f --- /dev/null +++ b/src/request.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_REQUEST_INFO_H__ +#define __CONTEXT_REQUEST_INFO_H__ + +#include +#include + +namespace ctx { + + class request_info { + public: + request_info(int type, const char* client, int req_id, const char* subj, const char* desc); + virtual ~request_info(); + + int get_type(); + int get_id(); + const char* get_zone_name(); + const char* get_client(); + const char* get_subject(); + ctx::json& get_description(); + virtual const char* get_app_id(); + + virtual bool reply(int error) = 0; + virtual bool reply(int error, ctx::json& request_result) = 0; + virtual bool reply(int error, ctx::json& request_result, ctx::json& data_read) = 0; + virtual bool publish(int error, ctx::json& data) = 0; + + protected: + int _type; + int _req_id; + std::string _zone_name; + std::string _client; + std::string _subject; + ctx::json _description; + }; + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_REQUEST_INFO_H__ */ diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..6af2a0a --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define _USE_ECORE_MAIN_LOOP_ + +#include +#include +#include +#include + +#include +#include "dbus_server_impl.h" +#include "db_mgr_impl.h" +#include "timer_mgr_impl.h" +#include "context_mgr_impl.h" +#include "zone_util_impl.h" +#include "access_control/privilege.h" +#include "context_trigger/trigger.h" +#include "server.h" + +#ifdef _USE_ECORE_MAIN_LOOP_ +#include +#else +static GMainLoop *mainloop = NULL; +#endif + +static bool started = false; + +static ctx::context_manager_impl *context_mgr = NULL; +static ctx::timer_manager_impl *timer_mgr = NULL; +static ctx::db_manager_impl *database_mgr = NULL; +static ctx::dbus_server_impl *dbus_handle = NULL; +static ctx::context_trigger *trigger = NULL; + +void ctx::server::run() +{ + if (started) { + _W("Started already"); + return; + } + + _I("Init MainLoop"); +#ifdef _USE_ECORE_MAIN_LOOP_ + ecore_init(); + ecore_main_loop_glib_integrate(); +#else + mainloop = g_main_loop_new(NULL, FALSE); +#endif + + bool result = false; + + _I("Init vasum context"); + result = ctx::zone_util::init(); + IF_FAIL_CATCH_TAG(result, _E, "Vasum context initialization failed"); + + _I("Init access control configuration"); + result = ctx::privilege_manager::init(); + IF_FAIL_CATCH_TAG(result, _E, "Access controller initialization failed"); + + _I("Init Timer Manager"); + timer_mgr = new(std::nothrow) ctx::timer_manager_impl(); + IF_FAIL_CATCH_TAG(timer_mgr, _E, "Memory allocation failed"); + timer_manager::set_instance(timer_mgr); + result = timer_mgr->init(); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Database Manager"); + database_mgr = new(std::nothrow) ctx::db_manager_impl(); + IF_FAIL_CATCH_TAG(database_mgr, _E, "Memory allocation failed"); + db_manager::set_instance(database_mgr); + result = database_mgr->init(); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Context Manager"); + context_mgr = new(std::nothrow) ctx::context_manager_impl(); + IF_FAIL_CATCH_TAG(context_mgr, _E, "Memory allocation failed"); + context_manager::set_instance(context_mgr); + result = context_mgr->init(); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Context Trigger"); + trigger = new(std::nothrow) ctx::context_trigger(); + IF_FAIL_CATCH_TAG(trigger, _E, "Memory allocation failed"); + result = trigger->init(context_mgr); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + _I("Init Dbus Connection"); + dbus_handle = new(std::nothrow) ctx::dbus_server_impl(); + IF_FAIL_CATCH_TAG(dbus_handle, _E, "Memory allocation failed"); + dbus_server::set_instance(dbus_handle); + result = dbus_handle->init(); + IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); + + // Start the main loop + started = true; + _I(CYAN("Context-Service Launched")); +#ifdef _USE_ECORE_MAIN_LOOP_ + ecore_main_loop_begin(); +#else + g_main_loop_run(mainloop); +#endif + + _I(CYAN("Terminating Context-Service")); + + _I("Release Context Trigger"); + trigger->release(); + + _I("Release Analyzer Manager"); + context_mgr->release(); + + _I("Release Dbus Connection"); + dbus_handle->release(); + + _I("Close the Database"); + database_mgr->release(); + + _I("Release Timer Manager"); + timer_mgr->release(); + + _I("Release Access control configuration"); + ctx::privilege_manager::release(); + + _I("Release Vasum context"); + ctx::zone_util::release(); + +#ifdef _USE_ECORE_MAIN_LOOP_ + ecore_shutdown(); +#else + g_main_loop_unref(mainloop); +#endif + + delete trigger; + delete context_mgr; + delete dbus_handle; + delete database_mgr; + delete timer_mgr; + return; + +CATCH: + _E(RED("Launching Failed")); + delete trigger; + delete context_mgr; + delete dbus_handle; + delete database_mgr; + delete timer_mgr; +} + +void ctx::server::send_request(ctx::request_info* request) +{ + if (!trigger->assign_request(request)) { + context_mgr->assign_request(request); + } +} + +static void signal_handler(int signo) +{ + _I("SIGNAL %d received", signo); + + // Stop the main loop +#ifdef _USE_ECORE_MAIN_LOOP_ + ecore_main_loop_quit(); +#else + g_main_loop_quit(mainloop); +#endif +} + +int main(int argc, char* argv[]) +{ + static struct sigaction signal_action; + signal_action.sa_handler = signal_handler; + sigemptyset(&signal_action.sa_mask); + + sigaction(SIGINT, &signal_action, NULL); + sigaction(SIGHUP, &signal_action, NULL); + sigaction(SIGTERM, &signal_action, NULL); + sigaction(SIGQUIT, &signal_action, NULL); + +#if !defined(GLIB_VERSION_2_36) + g_type_init(); +#endif + + ctx::server::run(); + + return EXIT_SUCCESS; +} diff --git a/src/server.h b/src/server.h new file mode 100644 index 0000000..1807998 --- /dev/null +++ b/src/server.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_SERVER_H__ +#define __CONTEXT_SERVER_H__ + +namespace ctx { + + class request_info; + + namespace server { + + void run(); + void send_request(request_info* request); + + }; /* namespace ctx::server */ + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_SERVER_H__ */ diff --git a/src/timer_mgr_impl.cpp b/src/timer_mgr_impl.cpp new file mode 100644 index 0000000..5e1dbcd --- /dev/null +++ b/src/timer_mgr_impl.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "timer_mgr_impl.h" + +#define IDENTIFIER "contextd" + +struct listener_info_s { + int timer_id; + ctx::timer_listener_iface* listener; + void* user_data; +}; + +typedef std::map listener_map_t; +static listener_map_t *listener_map = NULL; +static GMutex listener_map_mutex; + +static int generate_timer_id() +{ + static int tid = 0; + + tid ++; + if (tid < 0) { + _W("Overflow"); + tid = 1; + } + + return tid; +} + +static int alarm_expired_cb(alarm_id_t alarm_id, void* cb_data) +{ + int timer_id = 0; + ctx::timer_listener_iface *listener = NULL; + void* user_data; + + { + ctx::scope_mutex sm1(&listener_map_mutex); + listener_map_t::iterator mit = listener_map->find(alarm_id); + IF_FAIL_RETURN_TAG(mit != listener_map->end(), 0, _W, "Unknown Alarm %d", alarm_id); + timer_id = mit->second.timer_id; + listener = mit->second.listener; + user_data = mit->second.user_data; + } + + _D("Timer %d expired", timer_id); + bool repeat = listener->on_timer_expired(timer_id, user_data); + + if (!repeat) { + _D("Stop repeating Timer %d (Alarm %d)", timer_id, alarm_id); + ctx::scope_mutex sm2(&listener_map_mutex); + alarmmgr_remove_alarm(alarm_id); + listener_map->erase(alarm_id); + } + + return 0; +} + +ctx::timer_manager_impl::timer_manager_impl() + : initialized(false) +{ +} + +ctx::timer_manager_impl::~timer_manager_impl() +{ + release(); +} + +bool ctx::timer_manager_impl::init() +{ + IF_FAIL_RETURN_TAG(!initialized, true, _W, "Re-initialization"); + + listener_map = new(std::nothrow) listener_map_t; + IF_FAIL_RETURN_TAG(listener_map, false, _E, "Memory allocation failed"); + + int result = alarmmgr_init(IDENTIFIER); + IF_FAIL_RETURN_TAG(result == ALARMMGR_RESULT_SUCCESS, false, _E, "Alarm manager initialization failed"); + + result = alarmmgr_set_cb(alarm_expired_cb, this); + if (result != ALARMMGR_RESULT_SUCCESS) { + alarmmgr_fini(); + _E("Alarm callback registration failed"); + return false; + } + + alarmmgr_remove_all(); + initialized = true; + return true; +} + +void ctx::timer_manager_impl::release() +{ + if (initialized) { + alarmmgr_remove_all(); + alarmmgr_fini(); + delete listener_map; + listener_map = NULL; + initialized = false; + } +} + +int ctx::timer_manager_impl::set_for(int interval, timer_listener_iface* listener, void* user_data) +{ + IF_FAIL_RETURN_TAG(interval > 0 && listener, false, _E, "Invalid parameter"); + + alarm_id_t alarm_id; + int result; + +#if 0 + // Implementation for Tizen 2.3 + time_t trigger_time; + time(&trigger_time); + // time_t is in seconds.. It is the POSIX specification. + trigger_time += (interval * 60); + + result = alarmmgr_add_alarm(ALARM_TYPE_VOLATILE, trigger_time, interval * 60, NULL, &alarm_id); +#endif + + result = alarmmgr_add_periodic_alarm_withcb(interval, QUANTUMIZE, alarm_expired_cb, this, &alarm_id); + IF_FAIL_RETURN_TAG(result == ALARMMGR_RESULT_SUCCESS, ERR_OPERATION_FAILED, _E, "Alarm initialization failed"); + + ctx::scope_mutex sm(&listener_map_mutex); + + listener_info_s info; + info.timer_id = generate_timer_id(); + info.listener = listener; + info.user_data = user_data; + (*listener_map)[alarm_id] = info; + + _D("Timer %d was set for %dm interval", info.timer_id, interval); + + return info.timer_id; +} + +int ctx::timer_manager_impl::set_at(int hour, int min, int day_of_week, timer_listener_iface* listener, void* user_data) +{ + IF_FAIL_RETURN_TAG( + hour < 24 && hour >= 0 && + min < 60 && min >= 0 && + day_of_week > 0 && day_of_week <= timer_manager::EVERYDAY && + listener, false, _E, "Invalid parameter"); + + int repeat = 0; + if (day_of_week & timer_manager::SUN) repeat |= ALARM_WDAY_SUNDAY; + if (day_of_week & timer_manager::MON) repeat |= ALARM_WDAY_MONDAY; + if (day_of_week & timer_manager::TUE) repeat |= ALARM_WDAY_TUESDAY; + if (day_of_week & timer_manager::WED) repeat |= ALARM_WDAY_WEDNESDAY; + if (day_of_week & timer_manager::THU) repeat |= ALARM_WDAY_THURSDAY; + if (day_of_week & timer_manager::FRI) repeat |= ALARM_WDAY_FRIDAY; + if (day_of_week & timer_manager::SAT) repeat |= ALARM_WDAY_SATURDAY; + + alarm_entry_t *alarm_info = alarmmgr_create_alarm(); + IF_FAIL_RETURN_TAG(alarm_info, ERR_OPERATION_FAILED, _E, "Memory allocation failed"); + + time_t current_time; + struct tm current_tm; + time(¤t_time); + tzset(); + localtime_r(¤t_time, ¤t_tm); + + alarm_date_t alarm_time; + alarm_time.year = current_tm.tm_year + 1900; + alarm_time.month = current_tm.tm_mon + 1; + alarm_time.day = current_tm.tm_mday; + alarm_time.hour = hour; + alarm_time.min = min; + alarm_time.sec = 0; + + alarmmgr_set_time(alarm_info, alarm_time); + alarmmgr_set_repeat_mode(alarm_info, ALARM_REPEAT_MODE_WEEKLY, repeat); + alarmmgr_set_type(alarm_info, ALARM_TYPE_VOLATILE); + + alarm_id_t alarm_id; + int ret = alarmmgr_add_alarm_with_localtime(alarm_info, NULL, &alarm_id); + alarmmgr_free_alarm(alarm_info); + + IF_FAIL_RETURN_TAG(ret == ALARMMGR_RESULT_SUCCESS, ERR_OPERATION_FAILED, _E, "Alarm initialization failed"); + + ctx::scope_mutex sm(&listener_map_mutex); + + listener_info_s info; + info.timer_id = generate_timer_id(); + info.listener = listener; + info.user_data = user_data; + + (*listener_map)[alarm_id] = info; + + _D("Timer %d was set at %02d:%02d:00 (day of week: %#x)", info.timer_id, hour, min, day_of_week); + + return info.timer_id; +} + +void ctx::timer_manager_impl::remove(int timer_id) +{ + ctx::scope_mutex sm(&listener_map_mutex); + + listener_map_t::iterator it; + for (it = listener_map->begin(); it != listener_map->end(); ++it) { + if (it->second.timer_id == timer_id) { + alarmmgr_remove_alarm(it->first); + listener_map->erase(it); + _D("Timer %d was removed", timer_id); + break; + } + } +} diff --git a/src/timer_mgr_impl.h b/src/timer_mgr_impl.h new file mode 100644 index 0000000..63c9e93 --- /dev/null +++ b/src/timer_mgr_impl.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_TIMER_MANAGER_IMPL_H__ +#define __CONTEXT_TIMER_MANAGER_IMPL_H__ + +#include +#include + +namespace ctx { + + class timer_manager_impl : public timer_manager_iface { + private: + bool initialized; + + public: + timer_manager_impl(); + ~timer_manager_impl(); + + bool init(); + void release(); + + int set_for(int interval, timer_listener_iface* listener, void* user_data); + int set_at(int hour, int min, int day_of_week, timer_listener_iface* listener, void* user_data); + void remove(int timer_id); + + }; /* class timer_manager */ +} + +#endif /* __CONTEXT_TIMER_MANAGER_IMPL_H__ */ diff --git a/src/zone_util_impl.cpp b/src/zone_util_impl.cpp new file mode 100644 index 0000000..865f466 --- /dev/null +++ b/src/zone_util_impl.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "zone_util_impl.h" + +#define HOST_NAME "" + +static bool container_enabled = false; +static vsm_context_h _vsm_ctx = NULL; +static ctx::zone_util_impl* _instance = NULL; + +const char* ctx::zone_util_impl::default_zone() +{ + if (container_enabled) + return VSM_DEFAULT_ZONE; + + return HOST_NAME; +} + +void* ctx::zone_util_impl::join_by_name(const char* name) +{ + IF_FAIL_RETURN(container_enabled, NULL); + IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); + + if (name == NULL) { + _D("NULL zone name. The default zone will be used."); + name = VSM_DEFAULT_ZONE; + } + + vsm_zone_h target_zone = vsm_lookup_zone_by_name(_vsm_ctx, name); + IF_FAIL_RETURN_TAG(target_zone, NULL, _E, RED("Zone lookup failed")); + + vsm_zone_h current_zone = vsm_lookup_zone_by_pid(_vsm_ctx, getpid()); + IF_FAIL_RETURN_TAG(current_zone, NULL, _E, RED("Zone lookup failed")); + IF_FAIL_RETURN_TAG(target_zone != current_zone, NULL, _I, YELLOW("Already in the target zone %s"), name); + + _I(YELLOW("Joining to '%s'"), name); + return vsm_join_zone(target_zone); +} + +void* ctx::zone_util_impl::join_to_zone(void* zone) +{ + IF_FAIL_RETURN(container_enabled, NULL); + IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); + IF_FAIL_RETURN(zone, NULL); + vsm_zone_h target = static_cast(zone); + _I(YELLOW("Joining to '%s'"), vsm_get_zone_name(target)); + return vsm_join_zone(target); +} + +bool ctx::zone_util::init() +{ + system_info_get_platform_bool("tizen.org/feature/container", &container_enabled); + IF_FAIL_RETURN_TAG(_instance == NULL, true, _W, "Re-initialization"); + + _instance = new(std::nothrow) zone_util_impl(); + IF_FAIL_RETURN_TAG(_instance, false, _E, "Memory allocation failed"); + + if (container_enabled) { + _vsm_ctx = vsm_create_context(); + if (!_vsm_ctx) { + delete _instance; + _E("Memory allocation failed"); + return false; + } + } + + zone_util::set_instance(_instance); + return true; +} + +void ctx::zone_util::release() +{ + zone_util::set_instance(NULL); + + delete _instance; + _instance = NULL; + + if (_vsm_ctx) + vsm_cleanup_context(_vsm_ctx); + + _vsm_ctx = NULL; +} + +const char* ctx::zone_util::get_name_by_pid(pid_t pid) +{ + IF_FAIL_RETURN(container_enabled, HOST_NAME); + IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); + vsm_zone_h zn = vsm_lookup_zone_by_pid(_vsm_ctx, pid); + return vsm_get_zone_name(zn); +} diff --git a/src/zone_util_impl.h b/src/zone_util_impl.h new file mode 100644 index 0000000..ce2421c --- /dev/null +++ b/src/zone_util_impl.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__ +#define __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__ + +#include +#include +#include + +namespace ctx { + + class zone_util_impl : public zone_util_iface { + public: + zone_util_impl() {} + ~zone_util_impl() {} + void* join_by_name(const char* name); + void* join_to_zone(void* zone); + const char* default_zone(); + }; + + namespace zone_util { + bool init(); + void release(); + const char* get_name_by_pid(pid_t pid); + } + +} /* namespace ctx */ + +#endif /* End of __CONTEXT_VASUM_ZONE_UTIL_IMPL_H__ */ -- 2.7.4 From 2f9b17f1f979efdc3c42b2c406081f2b83563566 Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Mon, 15 Jun 2015 12:51:47 +0900 Subject: [PATCH 3/7] Disable vasum-related features Change-Id: I85a064c053eb2313dddef1cae30f91ce1002b617 Signed-off-by: Mu-Woong --- CMakeLists.txt | 1 - packaging/context-service.spec | 3 +-- src/context_trigger/rule_manager.cpp | 2 ++ src/zone_util_impl.cpp | 24 ++++++++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4233766..b45e459 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,6 @@ SET(DEPS appsvc pkgmgr-info capi-security-privilege-manager - vasum alarm-service notification clips diff --git a/packaging/context-service.spec b/packaging/context-service.spec index 879c694..42b8da0 100644 --- a/packaging/context-service.spec +++ b/packaging/context-service.spec @@ -2,7 +2,7 @@ Name: context-service Summary: Context-Service Version: 0.4.4 Release: 1 -Group: Framework/system +Group: System/Service License: Apache-2.0 Source0: %{name}-%{version}.tar.gz Source1: context-service.service @@ -18,7 +18,6 @@ BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(appsvc) BuildRequires: pkgconfig(pkgmgr-info) BuildRequires: pkgconfig(capi-security-privilege-manager) -BuildRequires: pkgconfig(vasum) BuildRequires: pkgconfig(alarm-service) BuildRequires: pkgconfig(notification) diff --git a/src/context_trigger/rule_manager.cpp b/src/context_trigger/rule_manager.cpp index f9c0d71..ca6647f 100644 --- a/src/context_trigger/rule_manager.cpp +++ b/src/context_trigger/rule_manager.cpp @@ -823,6 +823,7 @@ static void trigger_action_app_control(ctx::json& action, std::string zone) app_control_create(&app); app_control_import_from_bundle(app, appctl_bundle); +#if _ZONE_ENABLED_ char* op; app_control_get_operation(app, &op); app_control_set_operation(app, APP_SVC_OPERATION_JUMP); @@ -830,6 +831,7 @@ static void trigger_action_app_control(ctx::json& action, std::string zone) if (op != NULL) { app_control_add_extra_data(app, APP_SVC_K_JUMP_ORIGIN_OPERATION, op); } +#endif error = app_control_send_launch_request(app, NULL, NULL); if (error != APP_CONTROL_ERROR_NONE) { diff --git a/src/zone_util_impl.cpp b/src/zone_util_impl.cpp index 865f466..5a09ada 100644 --- a/src/zone_util_impl.cpp +++ b/src/zone_util_impl.cpp @@ -15,27 +15,34 @@ */ #include +#ifdef _ZONE_ENABLED_ #include +#endif #include #include #include "zone_util_impl.h" #define HOST_NAME "" +#if _ZONE_ENABLED_ static bool container_enabled = false; static vsm_context_h _vsm_ctx = NULL; +#endif static ctx::zone_util_impl* _instance = NULL; const char* ctx::zone_util_impl::default_zone() { +#if _ZONE_ENABLED_ if (container_enabled) return VSM_DEFAULT_ZONE; +#endif return HOST_NAME; } void* ctx::zone_util_impl::join_by_name(const char* name) { +#if _ZONE_ENABLED_ IF_FAIL_RETURN(container_enabled, NULL); IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); @@ -53,26 +60,36 @@ void* ctx::zone_util_impl::join_by_name(const char* name) _I(YELLOW("Joining to '%s'"), name); return vsm_join_zone(target_zone); +#else + return NULL; +#endif } void* ctx::zone_util_impl::join_to_zone(void* zone) { +#if _ZONE_ENABLED_ IF_FAIL_RETURN(container_enabled, NULL); IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); IF_FAIL_RETURN(zone, NULL); vsm_zone_h target = static_cast(zone); _I(YELLOW("Joining to '%s'"), vsm_get_zone_name(target)); return vsm_join_zone(target); +#else + return NULL; +#endif } bool ctx::zone_util::init() { +#if _ZONE_ENABLED_ system_info_get_platform_bool("tizen.org/feature/container", &container_enabled); IF_FAIL_RETURN_TAG(_instance == NULL, true, _W, "Re-initialization"); +#endif _instance = new(std::nothrow) zone_util_impl(); IF_FAIL_RETURN_TAG(_instance, false, _E, "Memory allocation failed"); +#if _ZONE_ENABLED_ if (container_enabled) { _vsm_ctx = vsm_create_context(); if (!_vsm_ctx) { @@ -81,6 +98,7 @@ bool ctx::zone_util::init() return false; } } +#endif zone_util::set_instance(_instance); return true; @@ -93,16 +111,22 @@ void ctx::zone_util::release() delete _instance; _instance = NULL; +#if _ZONE_ENABLED_ if (_vsm_ctx) vsm_cleanup_context(_vsm_ctx); _vsm_ctx = NULL; +#endif } const char* ctx::zone_util::get_name_by_pid(pid_t pid) { +#if _ZONE_ENABLED_ IF_FAIL_RETURN(container_enabled, HOST_NAME); IF_FAIL_RETURN_TAG(_vsm_ctx, NULL, _E, "Not initialized"); vsm_zone_h zn = vsm_lookup_zone_by_pid(_vsm_ctx, pid); return vsm_get_zone_name(zn); +#else + return HOST_NAME; +#endif } -- 2.7.4 From 9c4f504400ab3103a14c0f74015f3547566a7a69 Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Mon, 15 Jun 2015 14:56:30 +0900 Subject: [PATCH 4/7] Apply Cynara gdbus peer credentials Change-Id: Ib366e1a45d4fcafd98ed76c84d746876ef9a26e7 Signed-off-by: Mu-Woong --- CMakeLists.txt | 1 + packaging/context-service.spec | 2 ++ src/access_control/peer_creds.cpp | 39 +++++++++++++++++++++++++++++++ src/access_control/peer_creds.h | 31 +++++++++++++++++++++++++ src/client_request.cpp | 48 +++++---------------------------------- src/client_request.h | 8 +++---- src/dbus_server_impl.cpp | 15 ++++++++---- 7 files changed, 92 insertions(+), 52 deletions(-) create mode 100644 src/access_control/peer_creds.cpp create mode 100644 src/access_control/peer_creds.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b45e459..712a53f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ SET(DEPS capi-security-privilege-manager alarm-service notification + cynara-creds-gdbus clips context-common device-context-provider diff --git a/packaging/context-service.spec b/packaging/context-service.spec index 42b8da0..c7278be 100644 --- a/packaging/context-service.spec +++ b/packaging/context-service.spec @@ -21,6 +21,8 @@ BuildRequires: pkgconfig(capi-security-privilege-manager) BuildRequires: pkgconfig(alarm-service) BuildRequires: pkgconfig(notification) +BuildRequires: pkgconfig(cynara-creds-gdbus) + BuildRequires: pkgconfig(clips) BuildRequires: pkgconfig(context-common) BuildRequires: pkgconfig(context) diff --git a/src/access_control/peer_creds.cpp b/src/access_control/peer_creds.cpp new file mode 100644 index 0000000..afcb70f --- /dev/null +++ b/src/access_control/peer_creds.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "peer_creds.h" + +std::string ctx::peer_creds::get_smack_label(GDBusConnection *connection, const char *unique_name) +{ + gchar *client = NULL; + int err = cynara_creds_gdbus_get_client(connection, unique_name, CLIENT_METHOD_SMACK, &client); + IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, "", _E, "cynara_creds_gdbus_get_client() failed"); + + std::string ret = client; + g_free(client); + return ret; +} + +pid_t ctx::peer_creds::get_pid(GDBusConnection *connection, const char *unique_name) +{ + pid_t pid = -1; + int err = cynara_creds_gdbus_get_pid(connection, unique_name, &pid); + IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, -1, _E, "cynara_creds_gdbus_get_pid() failed"); + + return pid; +} diff --git a/src/access_control/peer_creds.h b/src/access_control/peer_creds.h new file mode 100644 index 0000000..e2704e5 --- /dev/null +++ b/src/access_control/peer_creds.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_PEER_CREDENTIALS_H__ +#define __CONTEXT_PEER_CREDENTIALS_H__ + +#include +#include +#include + +namespace ctx { + namespace peer_creds { + std::string get_smack_label(GDBusConnection *connection, const char *unique_name); + pid_t get_pid(GDBusConnection *connection, const char *unique_name); + } +} /* namespace ctx */ + +#endif /* End of __CONTEXT_PEER_CREDENTIALS_H__ */ diff --git a/src/client_request.cpp b/src/client_request.cpp index d1b8a95..1ce081d 100644 --- a/src/client_request.cpp +++ b/src/client_request.cpp @@ -16,56 +16,17 @@ #include #include -#include #include #include #include #include "zone_util_impl.h" #include "dbus_server_impl.h" -#include "access_control/privilege.h" #include "client_request.h" -ctx::client_request::client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv) +ctx::client_request::client_request(int type, const char* client, int req_id, const char* subj, const char* desc, GDBusMethodInvocation *inv) : request_info(type, client, req_id, subj, desc) , invocation(inv) { - gsize size; - int client_pid; - char *decoded = NULL; - const char *zone_name = NULL; - char *pkg_id = NULL; - - decoded = reinterpret_cast(g_base64_decode(cookie, &size)); - IF_FAIL_CATCH_TAG(decoded, _E, "Cookie decoding failed"); - - raw_cookie = decoded; - client_pid = security_server_get_cookie_pid(decoded); - pkg_id = security_server_get_smacklabel_cookie(decoded); - g_free(decoded); - IF_FAIL_CATCH_TAG(client_pid > 0, _E, "Invalid PID (%d)", client_pid); - - if (pkg_id == NULL) { - _W(RED("security_server_get_smacklabel_cookie() failed")); - char* app_id = NULL; - app_manager_get_app_id(client_pid, &app_id); - client_app_id = ctx::privilege_manager::get_pkg_id(app_id); - g_free(app_id); - } else { - //FIXME: Yes.. this is actually the package id - client_app_id = pkg_id; - g_free(pkg_id); - } - - zone_name = ctx::zone_util::get_name_by_pid(client_pid); - IF_FAIL_CATCH_TAG(zone_name, _E, RED("Zone name retrieval failed")); - _zone_name = zone_name; - - _SD(CYAN("Package: '%s' / Zone: '%s'"), client_app_id.c_str(), zone_name); - return; - -CATCH: - invocation = NULL; - throw ERR_OPERATION_FAILED; } ctx::client_request::~client_request() @@ -74,9 +35,12 @@ ctx::client_request::~client_request() g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); } -const char* ctx::client_request::get_cookie() +bool ctx::client_request::set_peer_creds(const char *smack_label, const char *zone) { - return raw_cookie.c_str(); + IF_FAIL_RETURN_TAG(smack_label && zone, false, _E, "Invalid parameter"); + client_app_id = smack_label; + _zone_name = zone; + return true; } const char* ctx::client_request::get_app_id() diff --git a/src/client_request.h b/src/client_request.h index fc4737e..bc7e8f3 100644 --- a/src/client_request.h +++ b/src/client_request.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,10 @@ namespace ctx { class client_request : public request_info { public: - client_request(int type, const char* client, int req_id, const char* subj, const char* desc, const char* cookie, GDBusMethodInvocation *inv); + client_request(int type, const char* client, int req_id, const char* subj, const char* desc, GDBusMethodInvocation *inv); ~client_request(); - const char* get_cookie(); + bool set_peer_creds(const char *smack_label, const char *zone); const char* get_app_id(); bool reply(int error); @@ -37,9 +37,7 @@ namespace ctx { private: GDBusMethodInvocation *invocation; - std::string raw_cookie; std::string client_app_id; - std::string exec_path; }; } /* namespace ctx */ diff --git a/src/dbus_server_impl.cpp b/src/dbus_server_impl.cpp index 1834df7..93400e7 100644 --- a/src/dbus_server_impl.cpp +++ b/src/dbus_server_impl.cpp @@ -22,6 +22,8 @@ #include #include "server.h" #include "client_request.h" +#include "access_control/peer_creds.h" +#include "zone_util_impl.h" #include "dbus_server_impl.h" static ctx::dbus_server_impl *_instance = NULL; @@ -78,17 +80,20 @@ static void handle_request(const char *sender, GVariant *param, GDBusMethodInvoc _I("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject); _SI("Input: %s", input); - //TODO: Parameter validation - ctx::client_request *request = NULL; try { - request = new ctx::client_request(req_type, sender, req_id, subject, input, cookie, invocation); + request = new ctx::client_request(req_type, sender, req_id, subject, input, invocation); } catch (std::bad_alloc& ba) { _E("Memory allocation failed"); g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); return; - } catch (int e) { - _E("Caught %d", e); + } + + std::string smack_label = ctx::peer_creds::get_smack_label(dbus_connection, sender); + pid_t pid = ctx::peer_creds::get_pid(dbus_connection, sender); + const char* zone = ctx::zone_util::get_name_by_pid(pid); + + if (smack_label.empty() || !request->set_peer_creds(smack_label.c_str(), zone)) { g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); return; } -- 2.7.4 From ff2ddf93f95cb434aaa762b43c7d24978947429c Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Mon, 15 Jun 2015 15:43:01 +0900 Subject: [PATCH 5/7] Remove redundant dependency to pkgmgr-info Change-Id: I0fedf4545cf1a62c3bcc98c3f3155a37c8ff9c7b Signed-off-by: Mu-Woong --- CMakeLists.txt | 1 - packaging/context-service.spec | 1 - src/access_control/privilege.cpp | 28 ---------------------------- src/access_control/privilege.h | 1 - 4 files changed, 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 712a53f..e2d02aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ SET(DEPS capi-system-info capi-appfw-app-manager appsvc - pkgmgr-info capi-security-privilege-manager alarm-service notification diff --git a/packaging/context-service.spec b/packaging/context-service.spec index c7278be..ff876cb 100644 --- a/packaging/context-service.spec +++ b/packaging/context-service.spec @@ -16,7 +16,6 @@ BuildRequires: pkgconfig(sqlite3) BuildRequires: pkgconfig(capi-system-info) BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(appsvc) -BuildRequires: pkgconfig(pkgmgr-info) BuildRequires: pkgconfig(capi-security-privilege-manager) BuildRequires: pkgconfig(alarm-service) BuildRequires: pkgconfig(notification) diff --git a/src/access_control/privilege.cpp b/src/access_control/privilege.cpp index 4af7fe4..4ea16ba 100644 --- a/src/access_control/privilege.cpp +++ b/src/access_control/privilege.cpp @@ -16,12 +16,8 @@ #include #include - -#include #include - #include - #include "config_loader.h" #include "privilege.h" @@ -73,27 +69,3 @@ bool ctx::privilege_manager::is_allowed(const char* pkg_id, const char* subject) _D("Privilege Check Result: %#x", ret); return (ret == PRIV_CHECKER_ERR_NONE); } - -std::string ctx::privilege_manager::get_pkg_id(const char* app_id) -{ - std::string pkg_id; - IF_FAIL_RETURN_TAG(app_id, pkg_id, _E, "Null AppId"); - - int ret; - pkgmgrinfo_appinfo_h app_info; - - ret = pkgmgrinfo_appinfo_get_appinfo(app_id, &app_info); - IF_FAIL_RETURN_TAG(ret == PMINFO_R_OK, pkg_id, _E, "Failed to get app_info"); - - char *pkg_name = NULL; - ret = pkgmgrinfo_appinfo_get_pkgname(app_info, &pkg_name); - if (ret != PMINFO_R_OK || pkg_name == NULL) { - pkgmgrinfo_appinfo_destroy_appinfo(app_info); - _E("Failed to get package name"); - return pkg_id; - } - - pkg_id = pkg_name; - pkgmgrinfo_appinfo_destroy_appinfo(app_info); - return pkg_id; -} diff --git a/src/access_control/privilege.h b/src/access_control/privilege.h index 3744945..b2edf6e 100644 --- a/src/access_control/privilege.h +++ b/src/access_control/privilege.h @@ -27,7 +27,6 @@ namespace ctx { void set(const char* subject, const char* priv); bool is_allowed(const char* pkg_id, const char* subject); - std::string get_pkg_id(const char* app_id); } /* namespace ctx::privilege_manager */ } /* namespace ctx */ -- 2.7.4 From b132b58f3f0ce7f3e6014656ede5e6cb219ab3f4 Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Fri, 19 Jun 2015 21:53:30 +0900 Subject: [PATCH 6/7] Sync with Tizen 2.4 Change-Id: Ibafb888f85c3d7fe7f85f2baaaf0fa135b7e798d Signed-off-by: Mu-Woong --- packaging/context-service.manifest | 10 +++------- src/access_control/peer_creds.cpp | 17 ++++++----------- src/access_control/peer_creds.h | 3 +-- src/dbus_server_impl.cpp | 12 ++++++------ 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/packaging/context-service.manifest b/packaging/context-service.manifest index 9ee0705..2e4fdb5 100644 --- a/packaging/context-service.manifest +++ b/packaging/context-service.manifest @@ -10,10 +10,6 @@ - - - - @@ -49,14 +45,14 @@ - - - + + + diff --git a/src/access_control/peer_creds.cpp b/src/access_control/peer_creds.cpp index afcb70f..e35dcfb 100644 --- a/src/access_control/peer_creds.cpp +++ b/src/access_control/peer_creds.cpp @@ -18,22 +18,17 @@ #include #include "peer_creds.h" -std::string ctx::peer_creds::get_smack_label(GDBusConnection *connection, const char *unique_name) +bool ctx::peer_creds::get(GDBusConnection *connection, const char *unique_name, std::string &smack_label, pid_t &pid) { gchar *client = NULL; int err = cynara_creds_gdbus_get_client(connection, unique_name, CLIENT_METHOD_SMACK, &client); - IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, "", _E, "cynara_creds_gdbus_get_client() failed"); + IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, false, _E, "cynara_creds_gdbus_get_client() failed"); - std::string ret = client; + smack_label = client; g_free(client); - return ret; -} -pid_t ctx::peer_creds::get_pid(GDBusConnection *connection, const char *unique_name) -{ - pid_t pid = -1; - int err = cynara_creds_gdbus_get_pid(connection, unique_name, &pid); - IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, -1, _E, "cynara_creds_gdbus_get_pid() failed"); + err = cynara_creds_gdbus_get_pid(connection, unique_name, &pid); + IF_FAIL_RETURN_TAG(err == CYNARA_API_SUCCESS, false, _E, "cynara_creds_gdbus_get_pid() failed"); - return pid; + return true; } diff --git a/src/access_control/peer_creds.h b/src/access_control/peer_creds.h index e2704e5..76b51d4 100644 --- a/src/access_control/peer_creds.h +++ b/src/access_control/peer_creds.h @@ -23,8 +23,7 @@ namespace ctx { namespace peer_creds { - std::string get_smack_label(GDBusConnection *connection, const char *unique_name); - pid_t get_pid(GDBusConnection *connection, const char *unique_name); + bool get(GDBusConnection *connection, const char *unique_name, std::string &smack_label, pid_t &pid); } } /* namespace ctx */ diff --git a/src/dbus_server_impl.cpp b/src/dbus_server_impl.cpp index 93400e7..21b10d3 100644 --- a/src/dbus_server_impl.cpp +++ b/src/dbus_server_impl.cpp @@ -89,16 +89,16 @@ static void handle_request(const char *sender, GVariant *param, GDBusMethodInvoc return; } - std::string smack_label = ctx::peer_creds::get_smack_label(dbus_connection, sender); - pid_t pid = ctx::peer_creds::get_pid(dbus_connection, sender); - const char* zone = ctx::zone_util::get_name_by_pid(pid); + std::string smack_label; + pid_t pid; - if (smack_label.empty() || !request->set_peer_creds(smack_label.c_str(), zone)) { - g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + if (ctx::peer_creds::get(dbus_connection, sender, smack_label, pid) && + request->set_peer_creds(smack_label.c_str(), ctx::zone_util::get_name_by_pid(pid))) { + ctx::server::send_request(request); return; } - ctx::server::send_request(request); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); } static void handle_method_call(GDBusConnection *conn, const gchar *sender, -- 2.7.4 From 65ea2741b2c13fa9cce0320ae0f0bb5595ef1466 Mon Sep 17 00:00:00 2001 From: Mu-Woong Date: Mon, 29 Jun 2015 18:32:29 +0900 Subject: [PATCH 7/7] Integrate changes from Tizen 2.4 - Add a smack rule for security API - Fix the prevent issue 451595 - Add a missing smack rule, media-data::cb - Remove mediahistory.admin privilege entries - Modify launching sequence. - Break the dependency to context-internal, which is removed from the snapshot. - Replace deprecated privilege checker API with security server - Update smack & privilege w.r.t. security and pkgmgr - Remove a smack rule 'ail::db' - Switch to ecore mainloop to support ecore event handling Change-Id: Ib21644a3466c54212b73c3b1bdb5d7cdd5570cf1 Signed-off-by: Mu-Woong --- CMakeLists.txt | 9 ++++- data/access-config.xml | 7 ---- packaging/context-service.manifest | 9 ++--- packaging/context-service.spec | 11 ++++-- packaging/context-service.xml | 1 + src/access_control/privilege.cpp | 15 +++++--- src/dbus_server_impl.cpp | 14 ++++++- src/server.cpp | 78 +++++++++++++++++++++----------------- src/server.h | 4 +- 9 files changed, 90 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2d02aa..5acec26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ SET(DEPS capi-system-info capi-appfw-app-manager appsvc - capi-security-privilege-manager alarm-service notification cynara-creds-gdbus @@ -44,6 +43,14 @@ ELSE("${ARCH}" STREQUAL "arm") ADD_DEFINITIONS("-D_EMULATOR") ENDIF("${ARCH}" STREQUAL "arm") +IF("${MAINLOOP}" STREQUAL "ecore") + ADD_DEFINITIONS("-D_USE_ECORE_MAIN_LOOP_") + SET(DEPS + ${DEPS} + ecore + ) +ENDIF("${MAINLOOP}" STREQUAL "ecore") + # Common Options INCLUDE(FindPkgConfig) INCLUDE_DIRECTORIES( diff --git a/data/access-config.xml b/data/access-config.xml index 2c4f6c0..5471300 100644 --- a/data/access-config.xml +++ b/data/access-config.xml @@ -40,13 +40,6 @@ - - - - - - - diff --git a/packaging/context-service.manifest b/packaging/context-service.manifest index 2e4fdb5..2bd5615 100644 --- a/packaging/context-service.manifest +++ b/packaging/context-service.manifest @@ -7,16 +7,13 @@ - + + - - - - @@ -53,6 +50,8 @@ + + diff --git a/packaging/context-service.spec b/packaging/context-service.spec index ff876cb..4c2c528 100644 --- a/packaging/context-service.spec +++ b/packaging/context-service.spec @@ -8,6 +8,9 @@ Source0: %{name}-%{version}.tar.gz Source1: context-service.service Source2: org.tizen.context.service +# For active window hooking, we need to use 'ecore' mainloop instead of the 'glib' mainloop. +%define MAINLOOP ecore + BuildRequires: cmake BuildRequires: sed BuildRequires: pkgconfig(vconf) @@ -16,16 +19,18 @@ BuildRequires: pkgconfig(sqlite3) BuildRequires: pkgconfig(capi-system-info) BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(appsvc) -BuildRequires: pkgconfig(capi-security-privilege-manager) BuildRequires: pkgconfig(alarm-service) BuildRequires: pkgconfig(notification) +%if "%{MAINLOOP}" == "ecore" +BuildRequires: pkgconfig(ecore) +%endif + BuildRequires: pkgconfig(cynara-creds-gdbus) BuildRequires: pkgconfig(clips) BuildRequires: pkgconfig(context-common) BuildRequires: pkgconfig(context) -BuildRequires: context-internal BuildRequires: pkgconfig(device-context-provider) BuildRequires: pkgconfig(statistics-context-provider) @@ -66,7 +71,7 @@ export CFLAGS+=" -DTIZEN_ENGINEER_MODE" export CXXFLAGS+=" -DTIZEN_ENGINEER_MODE" export FFLAGS+=" -DTIZEN_ENGINEER_MODE" -cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DARCH=%{ARCH} -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DPROFILE=%{?tizen_profile_name} +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DARCH=%{ARCH} -DMAJORVER=${MAJORVER} -DFULLVER=%{version} -DPROFILE=%{?tizen_profile_name} -DMAINLOOP=%{MAINLOOP} make %{?jobs:-j%jobs} %install diff --git a/packaging/context-service.xml b/packaging/context-service.xml index 9ed1f7b..61c5d87 100644 --- a/packaging/context-service.xml +++ b/packaging/context-service.xml @@ -17,5 +17,6 @@ http://tizen.org/privilege/location + http://tizen.org/privilege/packagemanager.info diff --git a/src/access_control/privilege.cpp b/src/access_control/privilege.cpp index 4ea16ba..e1d7936 100644 --- a/src/access_control/privilege.cpp +++ b/src/access_control/privilege.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include "config_loader.h" #include "privilege.h" @@ -62,10 +62,13 @@ bool ctx::privilege_manager::is_allowed(const char* pkg_id, const char* subject) return true; } - _D("PkgId: %s, Priv: %s", pkg_id, (it->second).c_str()); - std::string priv = "http://tizen.org/privilege/"; + std::string priv = "org.tizen.privilege."; priv += (it->second).c_str(); - int ret = privilege_checker_check_package_privilege(pkg_id, priv.c_str()); - _D("Privilege Check Result: %#x", ret); - return (ret == PRIV_CHECKER_ERR_NONE); + int result = 0; + int err = security_server_app_has_privilege(pkg_id, PERM_APP_TYPE_EFL, priv.c_str(), &result); + + _D("PkgId: %s, PrivName: %s, Enabled: %d", pkg_id, (it->second).c_str(), result); + IF_FAIL_RETURN_TAG(err == SECURITY_SERVER_API_SUCCESS, false, _E, "Privilege checking failed"); + + return (result == 1); } diff --git a/src/dbus_server_impl.cpp b/src/dbus_server_impl.cpp index 21b10d3..856e899 100644 --- a/src/dbus_server_impl.cpp +++ b/src/dbus_server_impl.cpp @@ -26,6 +26,8 @@ #include "zone_util_impl.h" #include "dbus_server_impl.h" +static bool conn_acquired = false; +static bool name_acquired = false; static ctx::dbus_server_impl *_instance = NULL; static GDBusConnection *dbus_connection = NULL; static guint dbus_owner_id = 0; @@ -98,7 +100,7 @@ static void handle_request(const char *sender, GVariant *param, GDBusMethodInvoc return; } - g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT)); + delete request; } static void handle_method_call(GDBusConnection *conn, const gchar *sender, @@ -130,12 +132,22 @@ static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer u raise(SIGTERM); } + conn_acquired = true; dbus_connection = conn; + + _I("Dbus connection acquired"); + + if (name_acquired) + ctx::server::activate(); } static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data) { + name_acquired = true; _SI("Dbus name acquired: %s", name); + + if (conn_acquired) + ctx::server::activate(); } static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data) diff --git a/src/server.cpp b/src/server.cpp index 6af2a0a..3ee030f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -//#define _USE_ECORE_MAIN_LOOP_ - #include #include #include @@ -45,13 +43,8 @@ static ctx::db_manager_impl *database_mgr = NULL; static ctx::dbus_server_impl *dbus_handle = NULL; static ctx::context_trigger *trigger = NULL; -void ctx::server::run() +void ctx::server::initialize() { - if (started) { - _W("Started already"); - return; - } - _I("Init MainLoop"); #ifdef _USE_ECORE_MAIN_LOOP_ ecore_init(); @@ -60,6 +53,26 @@ void ctx::server::run() mainloop = g_main_loop_new(NULL, FALSE); #endif + _I("Init Dbus Connection"); + dbus_handle = new(std::nothrow) ctx::dbus_server_impl(); + IF_FAIL_VOID_TAG(dbus_handle, _E, "Memory allocation failed"); + + dbus_server::set_instance(dbus_handle); + IF_FAIL_VOID_TAG(dbus_handle->init(), _E, "Initialization Failed"); + + // Start the main loop + _I(CYAN("Launching Context-Service")); +#ifdef _USE_ECORE_MAIN_LOOP_ + ecore_main_loop_begin(); +#else + g_main_loop_run(mainloop); +#endif +} + +void ctx::server::activate() +{ + IF_FAIL_VOID(!started); + bool result = false; _I("Init vasum context"); @@ -97,38 +110,43 @@ void ctx::server::run() result = trigger->init(context_mgr); IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); - _I("Init Dbus Connection"); - dbus_handle = new(std::nothrow) ctx::dbus_server_impl(); - IF_FAIL_CATCH_TAG(dbus_handle, _E, "Memory allocation failed"); - dbus_server::set_instance(dbus_handle); - result = dbus_handle->init(); - IF_FAIL_CATCH_TAG(result, _E, "Initialization Failed"); - - // Start the main loop started = true; _I(CYAN("Context-Service Launched")); + return; + +CATCH: + _E(RED("Launching Failed")); + + // Stop the main loop #ifdef _USE_ECORE_MAIN_LOOP_ - ecore_main_loop_begin(); + ecore_main_loop_quit(); #else - g_main_loop_run(mainloop); + g_main_loop_quit(mainloop); #endif +} +void ctx::server::release() +{ _I(CYAN("Terminating Context-Service")); - _I("Release Context Trigger"); - trigger->release(); + if (trigger) + trigger->release(); _I("Release Analyzer Manager"); - context_mgr->release(); + if (context_mgr) + context_mgr->release(); _I("Release Dbus Connection"); - dbus_handle->release(); + if (dbus_handle) + dbus_handle->release(); _I("Close the Database"); - database_mgr->release(); + if (database_mgr) + database_mgr->release(); _I("Release Timer Manager"); - timer_mgr->release(); + if (timer_mgr) + timer_mgr->release(); _I("Release Access control configuration"); ctx::privilege_manager::release(); @@ -147,15 +165,6 @@ void ctx::server::run() delete dbus_handle; delete database_mgr; delete timer_mgr; - return; - -CATCH: - _E(RED("Launching Failed")); - delete trigger; - delete context_mgr; - delete dbus_handle; - delete database_mgr; - delete timer_mgr; } void ctx::server::send_request(ctx::request_info* request) @@ -192,7 +201,8 @@ int main(int argc, char* argv[]) g_type_init(); #endif - ctx::server::run(); + ctx::server::initialize(); + ctx::server::release(); return EXIT_SUCCESS; } diff --git a/src/server.h b/src/server.h index 1807998..5e4cb68 100644 --- a/src/server.h +++ b/src/server.h @@ -23,7 +23,9 @@ namespace ctx { namespace server { - void run(); + void initialize(); + void activate(); + void release(); void send_request(request_info* request); }; /* namespace ctx::server */ -- 2.7.4