From 51dee2857db927c2be55c5eb6a2be8031f4b9082 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Thu, 12 Mar 2015 15:42:08 +0100 Subject: [PATCH] Added functions to file descriptor passing and joining to namespace [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 | 167 +++++++++++++++++++++++++++++++++++++------ common/utils/environment.hpp | 9 +++ common/utils/execute.cpp | 64 +++++++++++++---- common/utils/execute.hpp | 18 +++++ common/utils/make-clean.hpp | 53 ++++++++++++++ 5 files changed, 274 insertions(+), 37 deletions(-) create mode 100644 common/utils/make-clean.hpp diff --git a/common/utils/environment.cpp b/common/utils/environment.cpp index 9d3f75a..fc45321 100644 --- a/common/utils/environment.cpp +++ b/common/utils/environment.cpp @@ -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 @@ -34,7 +36,95 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#if !__GLIBC_PREREQ(2, 14) + +#include + +#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 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(); + iovec iov = make_clean(); + 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(CMSG_DATA(cmsg)); +} + +bool fdSend(int socket, int fd) +{ + msghdr msg = make_clean(); + struct iovec iov = make_clean(); + 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(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& caps) bool launchAsRoot(const std::function& 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& 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 diff --git a/common/utils/environment.hpp b/common/utils/environment.hpp index 2060e19..d63ac16 100644 --- a/common/utils/environment.hpp +++ b/common/utils/environment.hpp @@ -52,6 +52,15 @@ bool dropRoot(uid_t uid, gid_t gid, const std::vector& caps); */ bool launchAsRoot(const std::function& 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& fdFactory); } // namespace utils } // namespace vasum diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp index 5c9d1ce..4e8eebf 100644 --- a/common/utils/execute.cpp +++ b/common/utils/execute.cpp @@ -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& 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& 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(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) diff --git a/common/utils/execute.hpp b/common/utils/execute.hpp index 47db58a..8f7fd56 100644 --- a/common/utils/execute.hpp +++ b/common/utils/execute.hpp @@ -26,14 +26,32 @@ #define COMMON_UTILS_EXECUTE_HPP #include +#include 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& func, int& status); + +bool executeAndWait(const std::function& 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 index 0000000..1d551ef --- /dev/null +++ b/common/utils/make-clean.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Mateusz Malicki + * + * 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 +#include + +namespace vasum { +namespace utils { + +template +void make_clean(T& value) +{ + static_assert(std::is_pod::value, "make_clean require trivial and standard-layout"); + std::fill_n(reinterpret_cast(&value), sizeof(value), 0); +} + +template +T make_clean() +{ + T value; + make_clean(value); + return value; +} + +} // namespace utils +} // namespace vasum + + +#endif // COMMON_UTILS_MAKE_CLEAN_HPP -- 2.7.4