#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 {
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
*/
#include "config.hpp"
+#include "base-exception.hpp"
#include "utils/execute.hpp"
#include "logger/logger.hpp"
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)
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)
--- /dev/null
+/*
+ * 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