From: Mu-Woong Lee Date: Fri, 9 Jun 2017 09:40:02 +0000 (+0900) Subject: Dependency cleanup: add service & client manipulation classes X-Git-Tag: submit/tizen/20170609.130020^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F40%2F133240%2F3;p=platform%2Fcore%2Fcontext%2Fcontext-service.git Dependency cleanup: add service & client manipulation classes These classes were originally implemented in context-common. Change-Id: Id26d5dd7321de8062ae7126607b66bf0a5119666 Signed-off-by: Mu-Woong Lee --- diff --git a/packaging/context-service.spec b/packaging/context-service.spec index e53cb3b..ec68da9 100644 --- a/packaging/context-service.spec +++ b/packaging/context-service.spec @@ -1,6 +1,6 @@ Name: context-service Summary: Tizen Contextual Service Framework -Version: 1.0.1 +Version: 1.0.2 Release: 1 Group: Service/Context License: Apache-2.0 @@ -18,6 +18,9 @@ BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(capi-base-common) BuildRequires: pkgconfig(alarm-service) +BuildRequires: pkgconfig(cynara-creds-gdbus) +BuildRequires: pkgconfig(cynara-client) +BuildRequires: pkgconfig(cynara-session) BuildRequires: pkgconfig(context-common-server) BuildRequires: pkgconfig(context-app-history-server) @@ -32,9 +35,6 @@ BuildRequires: pkgconfig(capi-system-device) BuildRequires: pkgconfig(capi-appfw-app-manager) BuildRequires: pkgconfig(capi-appfw-package-manager) BuildRequires: pkgconfig(notification) -BuildRequires: pkgconfig(cynara-creds-gdbus) -BuildRequires: pkgconfig(cynara-client) -BuildRequires: pkgconfig(cynara-session) BuildRequires: pkgconfig(context-common-legacy) # --- diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 170e43e..4b474cc 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -2,12 +2,22 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) SET(target "contextd") -SET(DEPS libsystemd-login glib-2.0 gio-2.0 dlog capi-base-common alarm-service) -SET(DEPS ${DEPS} context-common-server) -SET(DEPS ${DEPS} context-app-history-server) -SET(DEPS ${DEPS} context-sensor-recorder-server) -SET(DEPS ${DEPS} context-store-server) -SET(DEPS ${DEPS} context-job-scheduler-server) +SET(DEPS + libsystemd-login + glib-2.0 + gio-2.0 + dlog + capi-base-common + alarm-service + cynara-creds-gdbus + cynara-client + cynara-session + context-common-server + context-app-history-server + context-sensor-recorder-server + context-store-server + context-job-scheduler-server +) SET(INCDIR "${CMAKE_INSTALL_INCLUDEDIR}/context-service") diff --git a/src/server/Credential.cpp b/src/server/Credential.cpp new file mode 100644 index 0000000..5bf2e14 --- /dev/null +++ b/src/server/Credential.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017 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 "Credential.h" + +#define CACHE_SIZE 100 + +using namespace ctx; + +namespace { + + GMutex __cynaraMutex; + + class PrivilegeChecker { + private: + cynara* __cynara; + + public: + PrivilegeChecker() : + __cynara(NULL) + { + ScopeMutex sm(&__cynaraMutex); + + int err; + cynara_configuration* conf = NULL; + + err = cynara_configuration_create(&conf); + IF_FAIL_VOID_TAG(err == CYNARA_API_SUCCESS, _E, "Cynara configuration creation failed"); + + err = cynara_configuration_set_cache_size(conf, CACHE_SIZE); + if (err != CYNARA_API_SUCCESS) { + _E("Cynara cache size set failed"); + cynara_configuration_destroy(conf); + return; + } + + err = cynara_initialize(&__cynara, conf); + cynara_configuration_destroy(conf); + if (err != CYNARA_API_SUCCESS) { + _E("Cynara initialization failed"); + __cynara = NULL; + return; + } + + _I("Cynara initialized"); + } + + ~PrivilegeChecker() + { + ScopeMutex sm(&__cynaraMutex); + + if (__cynara) + cynara_finish(__cynara); + + _I("Cynara deinitialized"); + } + + bool hasPrivilege(const char* client, const char* session, const char* user, const char* privil) + { + ScopeMutex sm(&__cynaraMutex); + + IF_FAIL_RETURN_TAG(__cynara, false, _E, "Cynara not initialized"); + int ret = cynara_check(__cynara, client, session, user, privil); + return (ret == CYNARA_API_ACCESS_ALLOWED); + } + }; +} + +Credential::Credential(GDBusConnection* conn, const std::string& busName) : + __pid(0), + __uid(ROOT_UID), + __session(NULL), + __user(NULL), + __valid(true) +{ + ScopeMutex sm(&__cynaraMutex); + + if (cynara_creds_gdbus_get_pid(conn, busName.c_str(), &__pid) == CYNARA_API_SUCCESS) + __session = cynara_session_from_pid(__pid); + + char* cid = NULL; + cynara_creds_gdbus_get_client(conn, busName.c_str(), CLIENT_METHOD_DEFAULT, &cid); + cynara_creds_gdbus_get_user(conn, busName.c_str(), USER_METHOD_DEFAULT, &__user); + + _SD("%d, %s, %s, %s", __pid, cid, __session, __user); + + if (!cid || !__session || !__user) { + _E("Peer credentialing failed"); + __valid = false; + } + + if (cid) { + __clientId = cid; + g_free(cid); + } + + if (__user) + __uid = static_cast(std::atoll(__user)); +} + +Credential::~Credential() +{ + g_free(__session); + g_free(__user); +} + +bool Credential::valid() const +{ + return __valid; +} + +bool Credential::hasPrivilege(const char* privil) const +{ + IF_FAIL_RETURN(privil, true); + IF_FAIL_RETURN_TAG(valid(), false, _W, "Unidentified peer"); + + static PrivilegeChecker privilegeChecker; + + _D("Checking '%s' for '%s'", privil, __clientId.c_str()); + if (!privilegeChecker.hasPrivilege(__clientId.c_str(), __session, __user, privil)) { + _W("Privilege denied"); + return false; + } + return true; +} + +uid_t Credential::getUid() const +{ + return __uid; +} + +const std::string& Credential::getClientId() const +{ + return __clientId; +} + +bool Credential::isSystem() const +{ + return util::isSystemUid(__uid); +} diff --git a/src/server/Credential.h b/src/server/Credential.h new file mode 100644 index 0000000..2c00f27 --- /dev/null +++ b/src/server/Credential.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 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_CREDENTIAL_H__ +#define __CONTEXT_CREDENTIAL_H__ + +#include +#include + +#define ROOT_UID 0 + +namespace ctx { + + class Credential { + public: + Credential(GDBusConnection* conn, const std::string& busName); + ~Credential(); + + bool valid() const; + bool hasPrivilege(const char* privil) const; + + uid_t getUid() const; + const std::string& getClientId() const; + bool isSystem() const; + + static bool isSystemUid(uid_t uid); + + private: + pid_t __pid; + uid_t __uid; + std::string __clientId; /* Tizen-Default: Smack label */ + char* __session; + char* __user; /* Tizen-Default: UID */ + bool __valid; + }; + +} + +#endif diff --git a/src/server/MethodCall.cpp b/src/server/MethodCall.cpp new file mode 100644 index 0000000..6d50861 --- /dev/null +++ b/src/server/MethodCall.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 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 "MethodCall.h" + +using namespace ctx; + +MethodCall::MethodCall(IClient* caller, const std::string& methodName, GVariant* param, GDBusMethodInvocation* invocation) : + __caller(caller), + __methodName(methodName), + __param(param), + __invocation(invocation) +{ + g_variant_ref(__param); +} + +MethodCall::~MethodCall() +{ + g_variant_unref(__param); + + if (!__invocation) + return; + + _E("Method call '%s' from '%s' is not handled yet", __methodName.c_str(), __caller->getBusName().c_str()); + + g_dbus_method_invocation_return_error_literal(__invocation, CTX_ERROR_DOMAIN, E_FAILED, ""); +} + +const std::string& MethodCall::getMethodName() +{ + return __methodName; +} + +GVariant* MethodCall::getParam() +{ + return __param; +} + +bool MethodCall::reply(GVariant* param) +{ + IF_FAIL_RETURN_TAG(__invocation, false, _E, "Replied already"); + + g_dbus_method_invocation_return_value(__invocation, param); + __invocation = NULL; + + return true; +} + +bool MethodCall::reply(int error) +{ + IF_FAIL_RETURN_TAG(__invocation, false, _D, "Replied already"); + + if (error == E_NONE) + return reply(static_cast(NULL)); + + g_dbus_method_invocation_return_error_literal(__invocation, CTX_ERROR_DOMAIN, error, ""); + __invocation = NULL; + + return true; +} + +void MethodCall::publish(const std::string& signalName, GVariant* param) +{ + getCaller().publish(signalName, param); +} + +bool MethodCall::hasPrivilege(const char* privil) +{ + return getCaller().hasPrivilege(privil); +} + +uid_t MethodCall::getUid() +{ + return getCaller().getUid(); +} + +const std::string& MethodCall::getCallerId() +{ + return getCaller().getId(); +} + +bool MethodCall::isSystem() +{ + return getCaller().isSystem(); +} + +IClient& MethodCall::getCaller() +{ + return *__caller; +} diff --git a/src/server/MethodCall.h b/src/server/MethodCall.h new file mode 100644 index 0000000..86a34f2 --- /dev/null +++ b/src/server/MethodCall.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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_METHOD_CALL_H__ +#define __CONTEXT_METHOD_CALL_H__ + +#include +#include +#include +#include + +namespace ctx { + + class MethodCall : public IMethodCall { + public: + MethodCall(IClient* caller, const std::string& methodName, GVariant* param, GDBusMethodInvocation* invocation); + ~MethodCall(); + + const std::string& getMethodName(); + + GVariant* getParam(); + + bool reply(GVariant* param); + bool reply(int error); + + void publish(const std::string& signalName, GVariant* param); + + bool hasPrivilege(const char* privil); + + uid_t getUid(); + + const std::string& getCallerId(); + + bool isSystem(); + + IClient& getCaller(); + + private: + IClient* __caller; + std::string __methodName; + GVariant* __param; + GDBusMethodInvocation* __invocation; + }; + +} + +#endif /* __CONTEXT_METHOD_CALL_H__ */ diff --git a/src/server/ServiceClient.cpp b/src/server/ServiceClient.cpp new file mode 100644 index 0000000..28ebcf9 --- /dev/null +++ b/src/server/ServiceClient.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017 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 "Credential.h" +#include "ServiceRunner.h" +#include "ServiceClient.h" + +using namespace ctx; + +ServiceClient::ServiceClient(ServiceRunner* runner, IMethodCallHandler* handler, const std::string& busName) : + __serviceRunner(runner), + __methodCallHandler(handler), + __busName(busName), + __credential(NULL) +{ +} + +ServiceClient::~ServiceClient() +{ + delete __credential; + delete __methodCallHandler; +} + +const std::string& ServiceClient::getBusName() +{ + return __busName; +} + +const std::string& ServiceClient::getId() +{ + return __credential->getClientId(); +} + +uid_t ServiceClient::getUid() +{ + return __credential->getUid(); +} + +bool ServiceClient::isSystem() +{ + return __credential->isSystem(); +} + +bool ServiceClient::isVerified() +{ + IF_FAIL_RETURN(__getCredential(), false); + return __credential->valid(); +} + +bool ServiceClient::hasPrivilege(const char* privil) +{ + return __credential->hasPrivilege(privil); +} + +bool ServiceClient::hasPrivileges(const std::vector& privil) +{ + for (auto& item : privil) { + if (!hasPrivilege(item.c_str())) + return false; + } + return true; +} + +void ServiceClient::publish(const std::string& signalName, GVariant* param) +{ + __serviceRunner->publish(__busName, signalName, param); +} + +IService* ServiceClient::getHostService() +{ + return __serviceRunner->getService(); +} + +ServiceRunner* ServiceClient::getHostServiceRunner() +{ + return __serviceRunner; +} + +void ServiceClient::onMethodCalled(IMethodCall* methodCall) +{ + __methodCallHandler->onMethodCalled(methodCall); +} + +void ServiceClient::onDisconnected() +{ + __methodCallHandler->onDisconnected(); +} + +bool ServiceClient::__getCredential() +{ + if (__credential) + return true; + + __credential = new Credential(__serviceRunner->getConnection(), __busName); + + return true; +} diff --git a/src/server/ServiceClient.h b/src/server/ServiceClient.h new file mode 100644 index 0000000..820cfcb --- /dev/null +++ b/src/server/ServiceClient.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 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_SERVICE_CLIENT_H__ +#define __CONTEXT_SERVICE_CLIENT_H__ + +#include +#include +#include +#include +#include +#include + +namespace ctx { + + class ServiceRunner; + class Credential; + + class ServiceClient : public IClient { + public: + ServiceClient(ServiceRunner* runner, IMethodCallHandler* callHandler, const std::string& busName); + ~ServiceClient(); + + const std::string& getBusName(); + const std::string& getId(); + + uid_t getUid(); + bool isSystem(); + + bool isVerified(); + + bool hasPrivilege(const char* privil); + bool hasPrivileges(const std::vector& privil); + + void publish(const std::string& signalName, GVariant* param); + + IService* getHostService(); + ServiceRunner* getHostServiceRunner(); + + void onMethodCalled(IMethodCall* methodCall); + void onDisconnected(); + + private: + bool __getCredential(); + + ServiceRunner* __serviceRunner; + IMethodCallHandler* __methodCallHandler; + std::string __busName; + Credential* __credential; + }; + +} + +#endif /* __CONTEXT_SERVICE_CLIENT_H__ */ diff --git a/src/server/ServiceLoader.cpp b/src/server/ServiceLoader.cpp index 19ca7a2..90f8ef1 100644 --- a/src/server/ServiceLoader.cpp +++ b/src/server/ServiceLoader.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -32,11 +33,11 @@ ServiceLoader::ServiceLoader() : ServiceLoader::~ServiceLoader() { - for (auto& svc : __userServices) { - delete svc; + for (auto& runner : __userServices) { + delete runner; } - for (auto& svc : __systemServices) { - delete svc; + for (auto& runner : __systemServices) { + delete runner; } } @@ -52,15 +53,15 @@ bool ServiceLoader::load(GDBusConnection* conn) void ServiceLoader::startSystem() { - for (auto& svc : __systemServices) { - svc->start(); + for (auto& runner : __systemServices) { + runner->start(); } } void ServiceLoader::stopSystem() { - for (auto& svc : __systemServices) { - svc->stop(); + for (auto& runner : __systemServices) { + runner->stop(); } } @@ -69,14 +70,14 @@ void ServiceLoader::startUser(uid_t uid) IF_FAIL_VOID(__activeUser != uid); _I("Starting services for %u", static_cast(uid)); - ServiceBase::setActiveUser(uid); + util::setActiveUid(uid); - for (auto& svc : __userServices) { - svc->start(); + for (auto& runner : __userServices) { + runner->start(); } - for (auto& svc : __systemServices) { - svc->notifyUserNew(); + for (auto& runner : __systemServices) { + runner->notifyUserNew(); } __activeUser = uid; @@ -87,14 +88,14 @@ void ServiceLoader::stopUser() IF_FAIL_VOID(__activeUser != ROOT_UID); _I("Stopping services for %u", static_cast(__activeUser)); - for (auto& svc : __userServices) { - svc->stop(); + for (auto& runner : __userServices) { + runner->stop(); } - ServiceBase::setActiveUser(ROOT_UID); + util::setActiveUid(ROOT_UID); - for (auto& svc : __systemServices) { - svc->notifyUserRemoved(); + for (auto& runner : __systemServices) { + runner->notifyUserRemoved(); } __activeUser = ROOT_UID; diff --git a/src/server/ServiceLoader.h b/src/server/ServiceLoader.h index 7492f14..ef85039 100644 --- a/src/server/ServiceLoader.h +++ b/src/server/ServiceLoader.h @@ -19,7 +19,9 @@ #include #include -#include +#include +#include "ServiceRunner.h" +#include "ServiceClient.h" namespace ctx { @@ -37,22 +39,27 @@ namespace ctx { private: uid_t __activeUser; - std::vector __userServices; - std::vector __systemServices; + std::vector __userServices; + std::vector __systemServices; template void __create(GDBusConnection* conn) { - ServiceBase *svc = NULL; + IService* svc = NULL; + try { - svc = new ServiceType(conn); + svc = new ServiceType(); } catch (const std::runtime_error& e) { _I(YELLOW("%s"), e.what()); return; } + + ServiceRunner* runner = new ServiceRunner(conn, svc); + svc->setServiceRunner(runner); + if (svc->isUserService()) { - __userServices.push_back(svc); + __userServices.push_back(runner); } else { - __systemServices.push_back(svc); + __systemServices.push_back(runner); } } }; diff --git a/src/server/ServiceRunner.cpp b/src/server/ServiceRunner.cpp new file mode 100644 index 0000000..da72446 --- /dev/null +++ b/src/server/ServiceRunner.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2017 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 "MethodCall.h" +#include "ServiceClient.h" +#include "ServiceRunner.h" + +using namespace ctx; + +ServiceRunner::ServiceRunner(GDBusConnection* conn, IService* service) : + __service(service), + __started(false), + __threadRunning(false), + __mainContext(NULL), + __mainLoop(NULL), + __gthread(NULL), + __connection(conn), + __objPath(CTX_DBUS_PATH), + __interface(CTX_DBUS_IFACE), + __nodeInfo(NULL), + __registrationId(0) +{ + __objPath += __service->getServiceName(); + __interface += __service->getServiceName(); +} + +ServiceRunner::~ServiceRunner() +{ +} + +GMainContext* ServiceRunner::getMainContext() +{ + return __mainContext; +} + +GDBusConnection* ServiceRunner::getConnection() +{ + return __connection; +} + +IService* ServiceRunner::getService() +{ + return __service; +} + +bool ServiceRunner::start() +{ + IF_FAIL_RETURN(!__started, true); + + _I("Preparing '%s'", __service->getServiceName()); + + __gthread = g_thread_new(__service->getServiceName(), __threadFunc, this); + IF_FAIL_RETURN_TAG(__gthread, false, _E, "Thread creation failed"); + + __started = true; + return true; +} + +void ServiceRunner::stop() +{ + IF_FAIL_VOID(__started); + __started = false; + + IF_FAIL_VOID(__threadRunning.load()); + + GSource *gSrc = g_idle_source_new(); + if (gSrc) { + // Tries to stop the main loop within its thread. + // In this way, already scheduled idle tasks are not discarded. + g_source_set_callback(gSrc, __stopMainLoop, this, NULL); + g_source_attach(gSrc, __mainContext); + g_source_unref(gSrc); + } else { + __stopMainLoop(this); + } + + _I("Joining the thread of '%s'", __service->getServiceName()); + g_thread_join(__gthread); + __gthread = NULL; +} + +gboolean ServiceRunner::__stopMainLoop(gpointer data) +{ + ServiceRunner* runner = static_cast(data); + _I(PURPLE("Stopping '%s'"), runner->__service->getServiceName()); + g_main_loop_quit(runner->__mainLoop); + return G_SOURCE_REMOVE; +} + +void ServiceRunner::publish(const std::string& busName, const std::string& signalName, GVariant* param) +{ + GError* gerr = NULL; + g_dbus_connection_emit_signal(__connection, + busName.c_str(), __objPath.c_str(), __interface.c_str(), + signalName.c_str(), param, &gerr); + HANDLE_GERROR(gerr); +} + +void ServiceRunner::notifyUserNew() +{ + IF_FAIL_VOID(__started); + IF_FAIL_VOID(__threadRunning.load()); + + GSource* gSrc = g_idle_source_new(); + IF_FAIL_VOID_TAG(gSrc, _E, E_STR_ALLOC); + + g_source_set_callback(gSrc, __onUserActivated, __service, NULL); + g_source_attach(gSrc, __mainContext); + g_source_unref(gSrc); +} + +void ServiceRunner::notifyUserRemoved() +{ + IF_FAIL_VOID(__started); + IF_FAIL_VOID(__threadRunning.load()); + + GSource* gSrc = g_idle_source_new(); + IF_FAIL_VOID_TAG(gSrc, _E, E_STR_ALLOC); + + g_source_set_callback(gSrc, __onUserDeactivated, __service, NULL); + g_source_attach(gSrc, __mainContext); + g_source_unref(gSrc); +} + +gboolean ServiceRunner::__onUserActivated(gpointer data) +{ + IService* svc = static_cast(data); + svc->onUserActivated(); + return G_SOURCE_REMOVE; +} + +gboolean ServiceRunner::__onUserDeactivated(gpointer data) +{ + IService* svc = static_cast(data); + svc->onUserDeactivated(); + return G_SOURCE_REMOVE; +} + +gpointer ServiceRunner::__threadFunc(gpointer data) +{ + ServiceRunner* runner = static_cast(data); + runner->__run(); + return NULL; +} + +void ServiceRunner::__run() +{ + if (!__init()) { + _E("Starting '%s' failed", __service->getServiceName()); + __release(); + return; + } + + __threadRunning.store(true); + + _I(CYAN("Starting '%s'"), __service->getServiceName()); + g_main_loop_run(__mainLoop); + + __threadRunning.store(false); + + __release(); +} + +bool ServiceRunner::__init() +{ + GError* gerr = NULL; + GDBusInterfaceVTable vtable; + + vtable.method_call = __onMethodCalled; + vtable.get_property = NULL; + vtable.set_property = NULL; + + __mainContext = g_main_context_new(); + IF_FAIL_RETURN_TAG(__mainContext, false, _E, "MainContext creation failed"); + + g_main_context_push_thread_default(__mainContext); + + __mainLoop = g_main_loop_new(__mainContext, FALSE); + IF_FAIL_RETURN_TAG(__mainLoop, false, _E, "MainLoop creation failed"); + + std::string introspection("" + __service->getMethodSpecs() + ""; + + __nodeInfo = g_dbus_node_info_new_for_xml(introspection.c_str(), NULL); + IF_FAIL_RETURN_TAG(__nodeInfo, false, _E, "NodeInfo creation failed"); + + __registrationId = g_dbus_connection_register_object(__connection, + __objPath.c_str(), __nodeInfo->interfaces[0], &vtable, this, NULL, &gerr); + HANDLE_GERROR(gerr); + IF_FAIL_RETURN_TAG(__registrationId > 0, false, _E, "Object registration failed"); + + return __service->prepare(); +} + +void ServiceRunner::__release() +{ + _D("Releasing '%s'", __service->getServiceName()); + + for (auto iter = __clients.begin(); iter != __clients.end(); ++iter) { + iter->second.client->onDisconnected(); + delete iter->second.client; + } + + __clients.clear(); + + __service->cleanup(); + + if (__registrationId > 0) + g_dbus_connection_unregister_object(__connection, __registrationId); + + if (__nodeInfo) + g_dbus_node_info_unref(__nodeInfo); + + if (__mainLoop) + g_main_loop_unref(__mainLoop); + + if (__mainContext) + g_main_context_unref(__mainContext); +} + +void ServiceRunner::__onMethodCalled(GDBusConnection* conn, const gchar* sender, + const gchar* path, const gchar* iface, const gchar* name, + GVariant* param, GDBusMethodInvocation* invocation, gpointer userData) +{ + ServiceRunner* runner = static_cast(userData); + runner->__onMethodCalled(sender, name, param, invocation); +} + +void ServiceRunner::__onMethodCalled(const std::string& sender, + const std::string& name, GVariant* param, GDBusMethodInvocation* invocation) +{ + _I("'%s' called '%s.%s'", sender.c_str(), __service->getServiceName(), name.c_str()); + + ServiceClient* client = __getClient(sender); + IF_FAIL_VOID(client); + + client->onMethodCalled(new MethodCall(client, name, param, invocation)); +} + +ServiceClient* ServiceRunner::__getClient(const std::string& busName) +{ + auto iter = __clients.find(busName); + + if (iter != __clients.end()) + return iter->second.client; + + IMethodCallHandler* callHandler = __service->createMethodCallHandler(); + IF_FAIL_RETURN(callHandler, NULL); + + ServiceClient* client = new ServiceClient(this, callHandler, busName); + if (!client->isVerified()) { + delete client; + return NULL; + } + + callHandler->setCaller(client); + + __ClientInfo info = {client, __watch(busName, client)}; + __clients[busName] = info; + + return client; +} + +void ServiceRunner::__onNameOwnerChanged(GDBusConnection* conn, const gchar* sender, + const gchar* path, const gchar* iface, const gchar* name, + GVariant* param, gpointer userData) +{ + ServiceClient* client = static_cast(userData); + client->getHostServiceRunner()->__removeClient(client->getBusName()); +} + +void ServiceRunner::__removeClient(const std::string& busName) +{ + _I("'%s' lost '%s'", __service->getServiceName(), busName.c_str()); + + auto iter = __clients.find(busName); + IF_FAIL_VOID(iter != __clients.end()); + + __ClientInfo info = iter->second; + __clients.erase(iter); + + __unwatch(info.watchId); + info.client->onDisconnected(); + delete info.client; +} + +unsigned int ServiceRunner::__watch(const std::string& busName, ServiceClient* client) +{ + return g_dbus_connection_signal_subscribe(__connection, + "org.freedesktop.DBus", "org.freedesktop.DBus", "NameOwnerChanged", "/org/freedesktop/DBus", + busName.c_str(), G_DBUS_SIGNAL_FLAGS_NONE, __onNameOwnerChanged, client, NULL); +} + +void ServiceRunner::__unwatch(unsigned int watchId) +{ + g_dbus_connection_signal_unsubscribe(__connection, watchId); +} diff --git a/src/server/ServiceRunner.h b/src/server/ServiceRunner.h new file mode 100644 index 0000000..967c13a --- /dev/null +++ b/src/server/ServiceRunner.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 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_SERVICE_RUNNER_H__ +#define __CONTEXT_SERVICE_RUNNER_H__ + +#include +#include +#include +#include +#include +#include + +namespace ctx { + + class ServiceClient; + + class ServiceRunner : public IServiceRunner { + public: + ServiceRunner(GDBusConnection* conn, IService* service); + ~ServiceRunner(); + + void setService(IService* service); + + bool start(); + void stop(); + + void notifyUserNew(); + void notifyUserRemoved(); + + void publish(const std::string& busName, const std::string& signalName, GVariant* param); + + GMainContext* getMainContext(); + GDBusConnection* getConnection(); + IService* getService(); + + private: + static gpointer __threadFunc(gpointer data); + + static void __onMethodCalled(GDBusConnection* conn, const gchar* sender, + const gchar* path, const gchar* iface, const gchar* name, + GVariant* param, GDBusMethodInvocation* invocation, gpointer userData); + + static void __onNameOwnerChanged(GDBusConnection* conn, const gchar* sender, + const gchar* path, const gchar* iface, const gchar* name, + GVariant* param, gpointer userData); + + static gboolean __onUserActivated(gpointer data); + + static gboolean __onUserDeactivated(gpointer data); + + static gboolean __stopMainLoop(gpointer data); + + void __onMethodCalled(const std::string& sender, + const std::string& name, GVariant* param, GDBusMethodInvocation* invocation); + + void __run(); + bool __init(); + void __release(); + + ServiceClient* __getClient(const std::string& busName); + void __removeClient(const std::string& busName); + unsigned int __watch(const std::string& busName, ServiceClient* client); + void __unwatch(unsigned int watchId); + + IService* __service; + bool __started; + std::atomic_bool __threadRunning; + + GMainContext* __mainContext; + GMainLoop* __mainLoop; + GThread* __gthread; + + GDBusConnection* __connection; + std::string __objPath; + std::string __interface; + + GDBusNodeInfo* __nodeInfo; + guint __registrationId; + + struct __ClientInfo { + ServiceClient* client; + unsigned int watchId; + }; + + std::map __clients; + }; + +} + +#endif /* __CONTEXT_SERVICE_RUNNER_H__ */