Selectively stop and restart the individual service when the active user changes 58/117258/2
authorMu-Woong Lee <muwoong.lee@samsung.com>
Fri, 3 Mar 2017 11:07:23 +0000 (20:07 +0900)
committerMu-Woong Lee <muwoong.lee@samsung.com>
Fri, 3 Mar 2017 11:23:44 +0000 (20:23 +0900)
Change-Id: I2a59124125ba4b6b20612f525aa56d93c1d78e80
Signed-off-by: Mu-Woong Lee <muwoong.lee@samsung.com>
CMakeLists.txt
packaging/context-service.spec
src/ActiveUserMonitor.cpp [new file with mode: 0644]
src/ActiveUserMonitor.h [new file with mode: 0644]
src/Main.cpp
src/ServiceLoader.cpp
src/ServiceLoader.h

index e00aed2..04043ff 100644 (file)
@@ -4,7 +4,7 @@ INCLUDE(GNUInstallDirs)
 
 SET(target "contextd")
 
-SET(DEPS "glib-2.0 gio-2.0 dlog capi-base-common alarm-service")
+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} sensor-recorder-server")
 SET(DEPS "${DEPS} context-store-server")
index 6b7080c..ba0d993 100644 (file)
@@ -9,6 +9,7 @@ Source1:        context-service.service
 Source2:       org.tizen.context.conf
 
 BuildRequires: cmake
+BuildRequires: pkgconfig(libsystemd-login)
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(gio-2.0)
 BuildRequires: pkgconfig(dlog)
diff --git a/src/ActiveUserMonitor.cpp b/src/ActiveUserMonitor.cpp
new file mode 100644 (file)
index 0000000..4571c08
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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 <systemd/sd-login.h>
+#include "ActiveUserMonitor.h"
+
+#define ROOT_UID 0
+
+using namespace ctx;
+
+ActiveUserMonitor::ActiveUserMonitor() :
+       __connection(NULL),
+       __activateUser(NULL),
+       __deactivateUser(NULL),
+       __activeUid(ROOT_UID),
+       __userNewSignalId(0),
+       __userRemovedSignalId(0)
+{
+}
+
+ActiveUserMonitor::~ActiveUserMonitor()
+{
+}
+
+void ActiveUserMonitor::start(GDBusConnection* conn, uid_cb_t activateUser, uid_cb_t deactivateUser)
+{
+       __connection = conn;
+       __activateUser = activateUser;
+       __deactivateUser = deactivateUser;
+
+       __userNewSignalId = g_dbus_connection_signal_subscribe(__connection,
+                       NULL, "org.freedesktop.login1.Manager", "UserNew", NULL,
+                       NULL, G_DBUS_SIGNAL_FLAGS_NONE, __onUserNew, this, NULL);
+
+       __userRemovedSignalId = g_dbus_connection_signal_subscribe(__connection,
+                       NULL, "org.freedesktop.login1.Manager", "UserRemoved", NULL,
+                       NULL, G_DBUS_SIGNAL_FLAGS_NONE, __onUserRemoved, this, NULL);
+
+       g_idle_add(__checkCurrentUser, this);
+}
+
+void ActiveUserMonitor::stop()
+{
+       g_dbus_connection_signal_unsubscribe(__connection, __userNewSignalId);
+       g_dbus_connection_signal_unsubscribe(__connection, __userRemovedSignalId);
+}
+
+void ActiveUserMonitor::__onUserNew(GDBusConnection* conn, const gchar* sender,
+               const gchar* path, const gchar* iface, const gchar* name,
+               GVariant* param, gpointer userData)
+{
+       uint32_t uid = 0;
+       g_variant_get_child(param, 0, "u", &uid);
+       IF_FAIL_VOID_TAG(uid > 0, _W, "UID == 0");
+
+       _D("UID: %u", uid);
+
+       ActiveUserMonitor* monitor = static_cast<ActiveUserMonitor*>(userData);
+
+       if (monitor->__activeUid == uid)
+               return;
+
+       if (monitor->__activeUid > ROOT_UID) {
+               _W("Over-activation of the user %u", uid);
+               monitor->__deactivateUser(monitor->__activeUid);
+       }
+
+       monitor->__activateUser(uid);
+       monitor->__activeUid = uid;
+}
+
+void ActiveUserMonitor::__onUserRemoved(GDBusConnection* conn, const gchar* sender,
+               const gchar* path, const gchar* iface, const gchar* name,
+               GVariant* param, gpointer userData)
+{
+       uint32_t uid = 0;
+       g_variant_get_child(param, 0, "u", &uid);
+       IF_FAIL_VOID_TAG(uid > 0, _W, "UID == 0");
+
+       _D("UID: %u", uid);
+
+       ActiveUserMonitor* monitor = static_cast<ActiveUserMonitor*>(userData);
+
+       if (monitor->__activeUid == ROOT_UID) {
+               _W("No active user");
+               return;
+       }
+
+       if (monitor->__activeUid != uid) {
+               _W("Mismatched uid");
+       }
+
+       monitor->__deactivateUser(uid);
+       monitor->__activeUid = ROOT_UID;
+}
+
+gboolean ActiveUserMonitor::__checkCurrentUser(gpointer userData)
+{
+       ActiveUserMonitor* monitor = static_cast<ActiveUserMonitor*>(userData);
+       IF_FAIL_RETURN(monitor->__activeUid == ROOT_UID, G_SOURCE_REMOVE);
+
+       uid_t* users = NULL;
+       int numUsers = sd_get_active_uids(&users);
+
+       if (numUsers > 0)
+               monitor->__activeUid = users[0];
+
+       g_free(users);
+
+       if (monitor->__activeUid != ROOT_UID) {
+               _D("UID: %u", monitor->__activeUid);
+               monitor->__activateUser(monitor->__activeUid);
+       }
+
+       return G_SOURCE_REMOVE;
+}
diff --git a/src/ActiveUserMonitor.h b/src/ActiveUserMonitor.h
new file mode 100644 (file)
index 0000000..f349c85
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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_ACTIVE_USER_MONITOR_H__
+#define __CONTEXT_ACTIVE_USER_MONITOR_H__
+
+#include <ContextTypes.h>
+
+namespace ctx {
+
+       class ActiveUserMonitor {
+
+               typedef void (*uid_cb_t)(uid_t);
+
+       public:
+               ActiveUserMonitor();
+               ~ActiveUserMonitor();
+
+               void start(GDBusConnection* conn, uid_cb_t activateUser, uid_cb_t deactivateUser);
+               void stop();
+
+       private:
+               static void __onUserNew(GDBusConnection* conn, const gchar* sender,
+                               const gchar* path, const gchar* iface, const gchar* name,
+                               GVariant* param, gpointer userData);
+
+               static void __onUserRemoved(GDBusConnection* conn, const gchar* sender,
+                               const gchar* path, const gchar* iface, const gchar* name,
+                               GVariant* param, gpointer userData);
+
+               static gboolean __checkCurrentUser(gpointer userData);
+
+               GDBusConnection* __connection;
+               uid_cb_t __activateUser;
+               uid_cb_t __deactivateUser;
+               uid_t __activeUid;
+               guint __userNewSignalId;
+               guint __userRemovedSignalId;
+       };
+
+}
+
+#endif
index dba5144..38c976d 100644 (file)
 #include <Timer.h>
 #include "DBusConnector.h"
 #include "ServiceLoader.h"
+#include "ActiveUserMonitor.h"
 #include "AlarmInitializer.h"
 
 using namespace ctx;
 
 namespace {
-       class Server {
+       class MainLoop {
        public:
                static bool start();
                static void stop();
        private:
-               Server() {}
+               MainLoop() {}
                static GMainLoop* __mainLoop;
        };
 }
 
-static gboolean __stopService(gpointer data)
-{
-       _I("Unloading services");
-       ServiceLoader::unload();
-       ::Server::stop();
-       return G_SOURCE_REMOVE;
-}
+GMainLoop* MainLoop::__mainLoop = NULL;
 
-GMainLoop* ::Server::__mainLoop = NULL;
-
-bool ::Server::start()
+bool MainLoop::start()
 {
        __mainLoop = g_main_loop_new(NULL, FALSE);
        IF_FAIL_RETURN_TAG(__mainLoop, false, _E, "Memory allocation failed");
@@ -58,35 +51,56 @@ bool ::Server::start()
        return true;
 }
 
-void ::Server::stop()
+void MainLoop::stop()
 {
        _I(PURPLE("Terminating..."));
        g_main_loop_quit(__mainLoop);
 }
 
+static ServiceLoader __serviceLoader;
+static ActiveUserMonitor __activeUserMonitor;
 static AlarmInitializer __alarmInit;
 
-static void __signalHandler(int signum)
+static void __activateUser(uid_t uid)
 {
-       _I(YELLOW("SIGNAL-%d: '%s'"), signum, strsignal(signum));
-       static bool terminated = false;
-       if (!terminated) {
-               g_idle_add(__stopService, NULL);
-               terminated = true;
-       }
+       __serviceLoader.startUser(uid);
 }
 
-static void __busAcquired(GDBusConnection* conn)
+static void __deactivateUser(uid_t uid)
+{
+       __serviceLoader.stopUser();
+}
+
+static void __startService(GDBusConnection* conn)
 {
        Timer::setDBusConnection(conn);
+       __activeUserMonitor.start(conn, __activateUser, __deactivateUser);
 
        _I("Loading services");
-       if (ServiceLoader::load(conn)) {
-               _I("Service loading successful");
+       if (!__serviceLoader.load(conn)) {
+               _E(RED("No service loaded"));
                return;
        }
 
-       _E(RED("No service loaded."));
+       _I("Service loading successful");
+       __serviceLoader.startSystem();
+}
+
+static gboolean __stopService(gpointer data)
+{
+       __activeUserMonitor.stop();
+
+       _I("Unloading services");
+       __serviceLoader.stopUser();
+       __serviceLoader.stopSystem();
+
+       MainLoop::stop();
+       return G_SOURCE_REMOVE;
+}
+
+static void __busAcquired(GDBusConnection* conn)
+{
+       __startService(conn);
 }
 
 static void __busLost(GDBusConnection* conn)
@@ -94,6 +108,16 @@ static void __busLost(GDBusConnection* conn)
        __stopService(NULL);
 }
 
+static void __signalHandler(int signum)
+{
+       _I(YELLOW("SIGNAL-%d: '%s'"), signum, strsignal(signum));
+       static bool terminated = false;
+       if (!terminated) {
+               g_idle_add(__stopService, NULL);
+               terminated = true;
+       }
+}
+
 int main(int argc, char* argv[])
 {
        static struct sigaction signalAction;
@@ -108,7 +132,7 @@ int main(int argc, char* argv[])
 
        DBusConnector dbusConnector(__busAcquired, __busLost);
 
-       ::Server::start();
+       MainLoop::start();
 
        return EXIT_SUCCESS;
 }
index 44fe578..9f4383a 100644 (file)
 
 #include "ServiceLoader.h"
 
+#define ROOT_UID 0
+
 using namespace ctx;
 
-std::vector<ServiceBase*> ServiceLoader::__services;
+ServiceLoader::ServiceLoader() :
+       __activeUser(ROOT_UID)
+{
+}
 
-ServiceLoader::ServiceLoader()
+ServiceLoader::~ServiceLoader()
 {
+       for (auto& svc : __userServices) {
+               delete svc;
+       }
+       for (auto& svc : __systemServices) {
+               delete svc;
+       }
 }
 
 bool ServiceLoader::load(GDBusConnection* conn)
@@ -36,24 +47,60 @@ bool ServiceLoader::load(GDBusConnection* conn)
        __create<ContextStoreService>(conn);
        //__create<JobSchedulerService>(conn);
 
-       if (__services.size() == 1) {
+       if ((__userServices.size() + __systemServices.size()) == 1) {
                _I("Switch to single-threading");
                ServiceBase::setSingleThreading();
        }
 
-       for (auto& svc : __services) {
-               if (!svc->start())
-                       return false;
+       return (!__systemServices.empty() || !__userServices.empty());
+}
+
+void ServiceLoader::startSystem()
+{
+       for (auto& svc : __systemServices) {
+               svc->start();
        }
+}
 
-       return !__services.empty();
+void ServiceLoader::stopSystem()
+{
+       for (auto& svc : __systemServices) {
+               svc->stop();
+       }
 }
 
-void ServiceLoader::unload()
+void ServiceLoader::startUser(uid_t uid)
 {
-       for (auto& svc : __services) {
+       IF_FAIL_VOID(__activeUser != uid);
+       _I("Starting services for %u", static_cast<unsigned int>(uid));
+
+       ServiceBase::setActiveUser(uid);
+
+       for (auto& svc : __userServices) {
+               svc->start();
+       }
+
+       for (auto& svc : __systemServices) {
+               svc->notifyUserNew();
+       }
+
+       __activeUser = uid;
+}
+
+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();
-               delete svc;
        }
-       __services.clear();
+
+       ServiceBase::setActiveUser(ROOT_UID);
+
+       for (auto& svc : __systemServices) {
+               svc->notifyUserRemoved();
+       }
+
+       __activeUser = ROOT_UID;
 }
index 2819d0f..5bedbb2 100644 (file)
@@ -25,15 +25,22 @@ namespace ctx {
 
        class ServiceLoader {
        public:
-               static bool load(GDBusConnection* conn);
-               static void unload();
-
-       private:
                ServiceLoader();
+               ~ServiceLoader();
+
+               bool load(GDBusConnection* conn);
 
-               static std::vector<ServiceBase*> __services;
+               void startUser(uid_t uid);
+               void stopUser();
+               void startSystem();
+               void stopSystem();
 
-               template<typename ServiceType> static void __create(GDBusConnection* conn)
+       private:
+               uid_t __activeUser;
+               std::vector<ServiceBase*> __userServices;
+               std::vector<ServiceBase*> __systemServices;
+
+               template<typename ServiceType> void __create(GDBusConnection* conn)
                {
                        ServiceBase *svc = NULL;
                        try {
@@ -42,7 +49,11 @@ namespace ctx {
                                _W("Service creation failed (%s)", e.what());
                                return;
                        }
-                       __services.push_back(svc);
+                       if (svc->isUserService()) {
+                               __userServices.push_back(svc);
+                       } else {
+                               __systemServices.push_back(svc);
+                       }
                }
        };