Dependency cleanup: add service & client manipulation classes 40/133240/3
authorMu-Woong Lee <muwoong.lee@samsung.com>
Fri, 9 Jun 2017 09:40:02 +0000 (18:40 +0900)
committerMu-Woong Lee <muwoong.lee@samsung.com>
Fri, 9 Jun 2017 11:21:06 +0000 (20:21 +0900)
These classes were originally implemented in context-common.

Change-Id: Id26d5dd7321de8062ae7126607b66bf0a5119666
Signed-off-by: Mu-Woong Lee <muwoong.lee@samsung.com>
12 files changed:
packaging/context-service.spec
src/server/CMakeLists.txt
src/server/Credential.cpp [new file with mode: 0644]
src/server/Credential.h [new file with mode: 0644]
src/server/MethodCall.cpp [new file with mode: 0644]
src/server/MethodCall.h [new file with mode: 0644]
src/server/ServiceClient.cpp [new file with mode: 0644]
src/server/ServiceClient.h [new file with mode: 0644]
src/server/ServiceLoader.cpp
src/server/ServiceLoader.h
src/server/ServiceRunner.cpp [new file with mode: 0644]
src/server/ServiceRunner.h [new file with mode: 0644]

index e53cb3b..ec68da9 100644 (file)
@@ -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)
 # ---
 
index 170e43e..4b474cc 100644 (file)
@@ -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 (file)
index 0000000..5bf2e14
--- /dev/null
@@ -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 <cynara-creds-gdbus.h>
+#include <cynara-session.h>
+#include <cynara-client.h>
+#include <ScopeMutex.h>
+#include <ServerUtil.h>
+#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<uid_t>(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 (file)
index 0000000..2c00f27
--- /dev/null
@@ -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 <string>
+#include <ContextTypes.h>
+
+#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 (file)
index 0000000..6d50861
--- /dev/null
@@ -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 <SharedUtil.h>
+#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<GVariant*>(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 (file)
index 0000000..86a34f2
--- /dev/null
@@ -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 <string>
+#include <ContextTypes.h>
+#include <IMethodCall.h>
+#include <IClient.h>
+
+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 (file)
index 0000000..28ebcf9
--- /dev/null
@@ -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<std::string>& 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 (file)
index 0000000..820cfcb
--- /dev/null
@@ -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 <vector>
+#include <string>
+#include <ContextTypes.h>
+#include <IClient.h>
+#include <IMethodCall.h>
+#include <IMethodCallHandler.h>
+
+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<std::string>& 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__ */
index 19ca7a2..90f8ef1 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ServerUtil.h>
 #include <AppHistoryService.h>
 #include <SensorRecorderService.h>
 #include <ContextStoreService.h>
@@ -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<unsigned int>(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<unsigned int>(__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;
index 7492f14..ef85039 100644 (file)
@@ -19,7 +19,9 @@
 
 #include <vector>
 #include <ContextTypes.h>
-#include <ServiceBase.h>
+#include <IService.h>
+#include "ServiceRunner.h"
+#include "ServiceClient.h"
 
 namespace ctx {
 
@@ -37,22 +39,27 @@ namespace ctx {
 
        private:
                uid_t __activeUser;
-               std::vector<ServiceBase*> __userServices;
-               std::vector<ServiceBase*> __systemServices;
+               std::vector<ServiceRunner*> __userServices;
+               std::vector<ServiceRunner*> __systemServices;
 
                template<typename ServiceType> 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 (file)
index 0000000..da72446
--- /dev/null
@@ -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 <ScopeMutex.h>
+#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<ServiceRunner*>(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<IService*>(data);
+       svc->onUserActivated();
+       return G_SOURCE_REMOVE;
+}
+
+gboolean ServiceRunner::__onUserDeactivated(gpointer data)
+{
+       IService* svc = static_cast<IService*>(data);
+       svc->onUserDeactivated();
+       return G_SOURCE_REMOVE;
+}
+
+gpointer ServiceRunner::__threadFunc(gpointer data)
+{
+       ServiceRunner* runner = static_cast<ServiceRunner*>(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("<node><interface name='");
+       introspection += __interface + "'>" + __service->getMethodSpecs() + "</interface></node>";
+
+       __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<ServiceRunner*>(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<ServiceClient*>(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 (file)
index 0000000..967c13a
--- /dev/null
@@ -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 <atomic>
+#include <string>
+#include <map>
+#include <ContextTypes.h>
+#include <IService.h>
+#include <IServiceRunner.h>
+
+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<std::string, __ClientInfo> __clients;
+       };
+
+}
+
+#endif /* __CONTEXT_SERVICE_RUNNER_H__ */