Added functions to file descriptor passing and joining to namespace 48/36748/6
authorMateusz Malicki <m.malicki2@samsung.com>
Thu, 12 Mar 2015 14:42:08 +0000 (15:42 +0100)
committerMateusz Malicki <m.malicki2@samsung.com>
Mon, 23 Mar 2015 13:27:00 +0000 (14:27 +0100)
[Feature]       Added functions to file descriptor passing and joining to namespace
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, run tests

Change-Id: I72ec4f65e469125d5add10cb87a6d044b4ff9b05

common/utils/environment.cpp
common/utils/environment.hpp
common/utils/execute.cpp
common/utils/execute.hpp
common/utils/make-clean.hpp [new file with mode: 0644]

index 9d3f75a..fc45321 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "utils/environment.hpp"
 #include "utils/execute.hpp"
+#include "utils/make-clean.hpp"
+#include "base-exception.hpp"
 #include "logger/logger.hpp"
 
 #include <cap-ng.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <cstring>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <map>
+#include <cassert>
+#include <features.h>
+
+#if !__GLIBC_PREREQ(2, 14)
+
+#include <sys/syscall.h>
+
+#ifdef __NR_setns
+static inline int setns(int fd, int nstype)
+{
+    // setns system call are available since v2.6.39-6479-g7b21fdd
+    return syscall(__NR_setns, fd, nstype);
+}
+#else
+#error "setns syscall isn't available"
+#endif
+
+#endif
+
+using namespace vasum::utils;
+
+namespace {
+
+const std::map<int, std::string> NAMESPACES = {
+    {CLONE_NEWIPC, "ipc"},
+    {CLONE_NEWNET, "net"},
+    {CLONE_NEWNS, "mnt"},
+    {CLONE_NEWPID, "pid"},
+    {CLONE_NEWUSER, "user"},
+    {CLONE_NEWUTS, "uts"}};
+
+int fdRecv(int socket)
+{
+    msghdr msg = make_clean<msghdr>();
+    iovec iov = make_clean<iovec>();
+    char cmsgBuff[CMSG_SPACE(sizeof(int))];
+
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    msg.msg_control = cmsgBuff;
+    msg.msg_controllen = sizeof(cmsgBuff);
+
+    int ret = recvmsg(socket, &msg, MSG_CMSG_CLOEXEC);
+    if (ret != 0 || msg.msg_flags & (MSG_TRUNC | MSG_ERRQUEUE | MSG_OOB | MSG_CTRUNC | MSG_EOR)) {
+        LOGE("Can't receive fd: ret: " << ret << ", flags: " << msg.msg_flags);
+        return -1;
+    }
+
+    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+    assert(cmsg->cmsg_level == SOL_SOCKET);
+    assert(cmsg->cmsg_type == SCM_RIGHTS);
+    assert(CMSG_NXTHDR(&msg, cmsg) == NULL);
+    return *reinterpret_cast<int*>(CMSG_DATA(cmsg));
+}
+
+bool fdSend(int socket, int fd)
+{
+    msghdr msg = make_clean<msghdr>();
+    struct iovec iov = make_clean<iovec>();
+    struct cmsghdr *cmsg = NULL;
+    char cmsgBuff[CMSG_SPACE(sizeof(int))];
+
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    msg.msg_control = cmsgBuff;
+    msg.msg_controllen = sizeof(cmsgBuff);
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    cmsg->cmsg_level = SOL_SOCKET;
+    cmsg->cmsg_type = SCM_RIGHTS;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+    *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = fd;
+
+    int ret = sendmsg(socket, &msg, 0);
+    if (ret < 0) {
+        LOGE("Can't send fd: ret: " << ret);
+        return false;
+    }
+    return true;
+}
 
+} // namespace
 
 namespace vasum {
 namespace utils {
@@ -88,43 +178,76 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector<unsigned int>& caps)
 bool launchAsRoot(const std::function<bool()>& func)
 {
     // TODO optimize if getuid() == 0
-    pid_t pid = fork();
-    if (pid < 0) {
-        LOGE("Fork failed: " << strerror(errno));
-        return false;
-    }
-
-    if (pid == 0) {
+    return executeAndWait([&func]() {
         if (::setuid(0) < 0) {
-            LOGW("Failed to become root: " << strerror(errno));
+            LOGW("Failed to become root: " << getSystemErrorMessage());
             _exit(EXIT_FAILURE);
         }
 
-        try {
-            if (!func()) {
-                LOGE("Failed to successfully execute func");
-                _exit(EXIT_FAILURE);
-            }
-        } catch (const std::exception& e) {
-            LOGE("Failed to successfully execute func: " << e.what());
+        if (!func()) {
+            LOGE("Failed to successfully execute func");
             _exit(EXIT_FAILURE);
         }
+    });
+}
 
-        _exit(EXIT_SUCCESS);
+bool joinToNs(int nsPid, int ns)
+{
+    auto ins = NAMESPACES.find(ns);
+    if (ins == NAMESPACES.end()) {
+        LOGE("Namespace isn't supported: " << ns);
+        return false;
     }
-
-    int status;
-    if (!waitPid(pid, status)) {
+    std::string nsPath = "/proc/" + std::to_string(nsPid) + "/ns/" + ins->second;
+    int nsFd = ::open(nsPath.c_str(), O_RDONLY);
+    if (nsFd == -1) {
+        LOGE("Can't open namesace: " + getSystemErrorMessage());
         return false;
     }
-    if (status != 0) {
-        LOGE("Function launched as root exited with status " << status);
+    int ret = setns(nsFd, ins->first);
+    if (ret != 0) {
+        LOGE("Can't set namesace: " + getSystemErrorMessage());
+        close(nsFd);
         return false;
     }
-
+    close(nsFd);
     return true;
 }
 
+int passNemaspacedFd(int nsPid, int ns, const std::function<int()>& fdFactory)
+{
+    int fds[2];
+    int ret = socketpair(PF_LOCAL, SOCK_RAW, 0, fds);
+    if (ret == -1) {
+        LOGE("Can't create socket pair: " << vasum::getSystemErrorMessage());
+        return -1;
+    }
+    bool success = executeAndWait([&, fds, nsPid, ns]() {
+        close(fds[0]);
+
+        int fd = -1;
+        if (joinToNs(nsPid, ns)) {
+            fd = fdFactory();
+        }
+        if (fd == -1) {
+            close(fds[1]);
+            _exit(EXIT_FAILURE);
+        }
+        LOGT("FD pass, send: " << fd);
+        fdSend(fds[1], fd);
+        close(fds[1]);
+        close(fd);
+    });
+
+    close(fds[1]);
+    int fd = -1;
+    if (success) {
+        fd = fdRecv(fds[0]);
+    }
+    close(fds[0]);
+    LOGT("FD pass, rcv: " << fd);
+    return fd;
+}
 
 } // namespace utils
 } // namespace vasum
index 2060e19..d63ac16 100644 (file)
@@ -52,6 +52,15 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector<unsigned int>& caps);
  */
 bool launchAsRoot(const std::function<bool()>& func);
 
+/**
+ * Join to namespace
+ */
+bool joinToNs(int nsPid, int ns);
+
+/**
+ * Pass file descriptor from namespace of some process
+ */
+int passNemaspacedFd(int nsPid, int ns, const std::function<int()>& fdFactory);
 
 } // namespace utils
 } // namespace vasum
index 5c9d1ce..4e8eebf 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 #include "config.hpp"
+#include "base-exception.hpp"
 #include "utils/execute.hpp"
 #include "logger/logger.hpp"
 
@@ -46,22 +47,65 @@ std::ostream& operator<< (std::ostream& out, const char* const* argv)
     return out;
 }
 
+bool isExecutionSuccessful(int status)
+{
+    if (!WIFEXITED(status)) {
+        if (WIFSIGNALED(status)) {
+            LOGE("Child terminated by signal, signal: " << WTERMSIG(status));
+        } else if (WIFSTOPPED(status)) {
+            LOGW("Child was stopped by signal " << WSTOPSIG(status));
+        } else {
+            LOGE("Child exited abnormally, status: " << status);
+        }
+        return false;
+    }
+    if (WEXITSTATUS(status) != EXIT_SUCCESS) {
+        LOGE("Child exit status: " << WEXITSTATUS(status));
+        return false;
+    }
+    return true;
+}
+
 } // namespace
 
-bool executeAndWait(const char* fname, const char* const* argv, int& status)
+bool executeAndWait(const std::function<void()>& func, int& status)
 {
-    LOGD("Execute " << fname << argv);
+    LOGD("Execute child process");
 
     pid_t pid = fork();
     if (pid == -1) {
-        LOGE("Fork failed");
+        LOGE("Fork failed: " << vasum::getSystemErrorMessage());
         return false;
     }
     if (pid == 0) {
+        func();
+        _exit(EXIT_SUCCESS);
+    }
+    return waitPid(pid, status);
+}
+
+bool executeAndWait(const std::function<void()>& func)
+{
+    int status;
+    if (!executeAndWait(func, status)) {
+        return false;
+    }
+    return isExecutionSuccessful(status);
+}
+
+
+bool executeAndWait(const char* fname, const char* const* argv, int& status)
+{
+    LOGD("Execute " << fname << argv);
+
+    bool success = executeAndWait([=]() {
         execv(fname, const_cast<char* const*>(argv));
         _exit(EXIT_FAILURE);
+    }, status);
+    if (!success) {
+        LOGW("Process " << fname << " has exited abnormally");
     }
-    return waitPid(pid, status);
+    return success;
 }
 
 bool executeAndWait(const char* fname, const char* const* argv)
@@ -70,17 +114,7 @@ bool executeAndWait(const char* fname, const char* const* argv)
     if (!executeAndWait(fname, argv, status)) {
         return false;
     }
-    if (status != EXIT_SUCCESS) {
-        if (WIFEXITED(status)) {
-            LOGW("Process " << fname << " has exited with status " << WEXITSTATUS(status));
-        } else if (WIFSIGNALED(status)) {
-            LOGW("Process " << fname << " was killed by signal " << WTERMSIG(status));
-        } else if (WIFSTOPPED(status)) {
-            LOGW("Process " << fname << " was stopped by signal " << WSTOPSIG(status));
-        }
-        return false;
-    }
-    return true;
+    return isExecutionSuccessful(status);
 }
 
 bool waitPid(pid_t pid, int& status)
index 47db58a..8f7fd56 100644 (file)
 #define COMMON_UTILS_EXECUTE_HPP
 
 #include <sys/types.h>
+#include <functional>
 
 namespace vasum {
 namespace utils {
 
+/**
+ * Execute binary
+ */
+///@{
 bool executeAndWait(const char* fname, const char* const* argv);
 
 bool executeAndWait(const char* fname, const char* const* argv, int& status);
+///@}
+
+/**
+ * Execute function in child process
+ */
+///@{
+bool executeAndWait(const std::function<void()>& func, int& status);
+
+bool executeAndWait(const std::function<void()>& func);
+///@}
 
+/**
+ * Wait until child processes ends
+ */
 bool waitPid(pid_t pid, int& status);
 
 } // namespace utils
diff --git a/common/utils/make-clean.hpp b/common/utils/make-clean.hpp
new file mode 100644 (file)
index 0000000..1d551ef
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Mateusz Malicki <m.malicki@samsung.com>
+ *
+ *  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
+ */
+
+/**
+ * @file
+ * @author  Mateusz Malicki (m.malicki2@samsung.com)
+ * @brief   Function used to initialize C structures
+ */
+
+#ifndef COMMON_UTILS_MAKE_CLEAN_HPP
+#define COMMON_UTILS_MAKE_CLEAN_HPP
+
+#include <algorithm>
+#include <type_traits>
+
+namespace vasum {
+namespace utils {
+
+template<class T>
+void make_clean(T& value)
+{
+    static_assert(std::is_pod<T>::value, "make_clean require trivial and standard-layout");
+    std::fill_n(reinterpret_cast<char*>(&value), sizeof(value), 0);
+}
+
+template<class T>
+T make_clean()
+{
+    T value;
+    make_clean(value);
+    return value;
+}
+
+} // namespace utils
+} // namespace vasum
+
+
+#endif // COMMON_UTILS_MAKE_CLEAN_HPP