TemporaryTestUser: encapsulate communication with gumd in separate process 75/66575/2
authorRafal Krypa <r.krypa@samsung.com>
Tue, 19 Apr 2016 12:26:44 +0000 (14:26 +0200)
committerRafal Krypa <r.krypa@samsung.com>
Tue, 19 Apr 2016 13:50:40 +0000 (15:50 +0200)
Gumd interface libgum has nasty side effects on the calling process.
On the first call to gumd service it allocates persistent resources
in the calling thread that are not freed and are used for subsequent
calls to gumd. These resources include a new thread, socket connection
and two eventfds.

This doesn't play well with process that changes it's Smack label and
DAC credentials back and forth. It also breaks encapsulation between
test cases, leading to nasty "gumd timeout" issues.

This patch attempts to fix this issue by dedicated a separate process
for calling libgum functions. The TemporaryTestUser class will no longer
call libgum directly, but delegate the calls to the dedicated process.

Change-Id: Id111d5ef79d65135e3b378582a220f168f8a2284
Signed-off-by: Rafal Krypa <r.krypa@samsung.com>
src/common/temp_test_user.cpp
src/common/temp_test_user.h

index f6aa6c1d43d25ac6ca79e67ae46eaa12ebb9f990..198645c8c2d195a2ebdab681cbfb4a8574bee81e 100644 (file)
 /*
  * @file        temp_test_user.cpp
  * @author      Jan Cybulski (j.cybulski@partner.samsung.com)
+ * @author      Rafal Krypa (j.cybulski@samsung.com)
  * @version     1.0
  * @brief       File with class for users management
  */
 
 
+#include <memory.h>
+#include <tests_common.h>
 #include <temp_test_user.h>
 #include <glib-object.h>
 #include <dpl/test/test_runner.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
 
 void TemporaryTestUser::create(void)
 {
-    if (m_guser) {
+    if (m_uid != 0)
         remove();
-    };
-
-    m_guser = gum_user_create_sync (m_offline);
-    RUNNER_ASSERT_MSG(m_guser != nullptr, "Failed to create gumd user object");
-    g_object_set(G_OBJECT(m_guser), "usertype", m_userType, NULL);
-    g_object_set(G_OBJECT(m_guser), "username", m_userName.c_str(), NULL);
-    gboolean added = gum_user_add_sync(m_guser);
-    RUNNER_ASSERT_MSG(added, "Failed to add user");
-    g_object_get(G_OBJECT(m_guser), "uid", &m_uid, NULL);
+
+    bool ret = m_runner.userCreate(m_userName, m_userType, m_offline, m_uid, m_gid);
+    RUNNER_ASSERT_MSG(ret, "Failed to add user");
     RUNNER_ASSERT_MSG(m_uid != 0, "Something strange happened during user creation. uid == 0.");
-    g_object_get(G_OBJECT(m_guser), "gid", &m_gid, NULL);
     RUNNER_ASSERT_MSG(m_gid != 0, "Something strange happened during user creation. gid == 0.");
 }
 
 void TemporaryTestUser::remove(void)
 {
-    if(m_guser){
-        gum_user_delete_sync(m_guser, TRUE);
-        g_object_unref(m_guser);
-        m_guser = nullptr;
-    }
+    if (m_uid == 0)
+        return;
+
+    m_runner.userRemove(m_uid, m_offline);
+    m_uid = m_gid = 0;
 }
 
 TemporaryTestUser::~TemporaryTestUser()
 {
     this->remove();
 }
+
+TemporaryTestUser::GumdRunner TemporaryTestUser::m_runner = TemporaryTestUser::GumdRunner();
+
+TemporaryTestUser::GumdRunner::GumdRunner()
+{
+    int sock[2];
+    int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+    RUNNER_ASSERT_MSG(ret != -1, "socketpair() failed");
+
+    pid_t pid = fork();
+    RUNNER_ASSERT_MSG(pid != -1, "fork() failed");
+    if (pid) { // parent
+        m_sock = sock[0];
+        close(sock[1]);
+        return;
+    } else { // child
+        m_sock = sock[1];
+        close(sock[0]);
+        prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // Don't outlive parent process
+        run(); // Never returns
+    }
+}
+
+TemporaryTestUser::GumdRunner::~GumdRunner()
+{
+    close(m_sock);
+}
+
+bool TemporaryTestUser::GumdRunner::userCreate(std::string userName,
+    GumUserType userType, bool offline, uid_t &uid, gid_t &gid)
+{
+    put(static_cast<int>(opType::USER_CREATE));
+    put(userName.size());
+    put(userName.c_str(), userName.size());
+    put(userType);
+    put(offline);
+
+    bool ret = get<bool>();
+    if (ret) {
+        uid = get<uid_t>();
+        gid = get<gid_t>();
+    }
+    return ret;
+}
+
+bool TemporaryTestUser::GumdRunner::userRemove(uid_t uid, bool offline)
+{
+    put(static_cast<int>(opType::USER_REMOVE));
+    put(uid);
+    put(offline);
+    return get<bool>();
+}
+
+
+void TemporaryTestUser::GumdRunner::run()
+{
+    DEFINE_SMARTPTR(g_object_unref, GumUser, GumUserPtr);
+
+    while (true) {
+        switch(static_cast<opType>(get<int>())) {
+        case opType::USER_CREATE:
+            {
+                size_t userNameLen = get<size_t>();
+                std::unique_ptr<char[]> userNamePtr(new char[userNameLen + 1]);
+                get(userNamePtr.get(), userNameLen);
+                userNamePtr[userNameLen] = '\0';
+                GumUserType userType = get<GumUserType>();
+                bool offline = get<bool>();
+
+                bool result = false;
+                uid_t uid;
+                gid_t gid;
+
+                GumUserPtr userPtr(gum_user_create_sync(offline));
+                if (userPtr) {
+                    g_object_set(G_OBJECT(userPtr.get()),
+                        "username", userNamePtr.get(),
+                        "usertype", userType,
+                        nullptr);
+                    gboolean ret = gum_user_add_sync(userPtr.get());
+                    if (ret == TRUE) {
+                        g_object_get(G_OBJECT(userPtr.get()), "uid", &uid, nullptr);
+                        g_object_get(G_OBJECT(userPtr.get()), "gid", &gid, nullptr);
+                        result = true;
+                    }
+                }
+
+                put(result);
+                if (result) {
+                    put(uid);
+                    put(gid);
+                }
+            }
+            break;
+
+        case opType::USER_REMOVE:
+            {
+                uid_t uid = get<uid_t>();
+                bool offline = get<bool>();
+                bool result = false;
+
+                GumUserPtr userPtr(gum_user_get_sync(uid, offline));
+                if (userPtr) {
+                    gboolean ret = gum_user_delete_sync(userPtr.get(), TRUE);
+                    result = (ret == TRUE);
+                }
+                put(static_cast<bool>(result));
+            }
+            break;
+        }
+    }
+}
+
+void TemporaryTestUser::GumdRunner::get(void *buf, size_t cnt)
+{
+    int ret;
+    for (size_t pos = 0; pos < cnt; pos += ret) {
+        ret = TEMP_FAILURE_RETRY(
+            read(m_sock, static_cast<char *>(buf) + pos, cnt - pos));
+        RUNNER_ASSERT_MSG(ret != -1, "read() failed");
+    }
+}
+
+void TemporaryTestUser::GumdRunner::put(const void *buf, size_t cnt)
+{
+    int ret;
+    for (size_t pos = 0; pos < cnt; pos += ret) {
+        ret = TEMP_FAILURE_RETRY(
+            write(m_sock, static_cast<const char *>(buf) + pos, cnt - pos));
+        RUNNER_ASSERT_MSG(ret != -1, "write() failed");
+    }
+}
+
+template<typename T>
+T TemporaryTestUser::GumdRunner::get()
+{
+    T x;
+    get(&x, sizeof(x));
+    return std::move(x);
+}
+
+template<typename T>
+void TemporaryTestUser::GumdRunner::put(const T x)
+{
+    put(&x, sizeof(x));
+}
index 120b21b883212c462a6cc2c11f69553c07c36a79..72e740891aefb94ad2f7256a3e619038803c3052 100644 (file)
@@ -30,7 +30,6 @@ public:
             m_gid(0),
             m_userName(userName),
             m_userType(userType),
-            m_guser(nullptr),
             m_offline(offline)
             {};
     ~TemporaryTestUser();
@@ -41,12 +40,32 @@ public:
     void getUidString(std::string& uidstr) const {uidstr =  std::to_string(static_cast<unsigned int>(m_uid));}
     const std::string& getUserName() const {return m_userName;}
     GumUserType getUserType() const {return m_userType;}
+
 private:
+    class GumdRunner {
+    public:
+        GumdRunner();
+        ~GumdRunner();
+        bool userCreate(std::string userName, GumUserType userType, bool offline,
+            uid_t &uid, gid_t &gid);
+        bool userRemove(uid_t uid, bool offline);
+
+    private:
+        enum class opType {USER_CREATE, USER_REMOVE};
+        int m_sock;
+
+        void run();
+        void get(void *buf, size_t count);
+        void put(const void *buf, size_t count);
+        template<typename T> T get();
+        template<typename T> void put(const T x);
+    };
+
+    static GumdRunner m_runner;
     uid_t m_uid;
     uid_t m_gid;
     std::string m_userName;
     GumUserType m_userType;
-    GumUser *m_guser;
     bool m_offline;
 };