From: Lukasz Pawelczyk Date: Wed, 29 Jul 2015 11:27:25 +0000 (+0200) Subject: lxcpp: Initial implementation of the start API call X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=61e31300c53fb090f48930f4d4462e6bc9218be1;p=platform%2Fcore%2Fsecurity%2Fvasum.git lxcpp: Initial implementation of the start API call [Feature] Initial implementation of the start API call. [Verification] Build, install, run tests Significant changes related to the start API call: - Start command that daemonizes and execs guard binary - Guard process implementation that execs the container's init Additional changes in this commit supporting the Start API call: - API extension to Channel class, support close on exec and be able to survive (as a FD) exec() call - set close on exec in persistent file logger - new logger helper to setup the logger - add pid to the log format Change-Id: I2d9648e2a861add2aa1bd1d66587bff2a109cc9c --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b98eee..0409167 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,10 +143,10 @@ ENDIF(NOT DEFINED PYTHON_SITELIB) SET(COMMON_FOLDER ${PROJECT_SOURCE_DIR}/common) SET(LIBS_FOLDER ${PROJECT_SOURCE_DIR}/libs) SET(LOGGER_FOLDER ${PROJECT_SOURCE_DIR}/libs/logger) -SET(LXCPP_FOLDER ${PROJECT_SOURCE_DIR}/libs/lxcpp) SET(DBUS_FOLDER ${PROJECT_SOURCE_DIR}/libs/dbus) SET(CONFIG_FOLDER ${PROJECT_SOURCE_DIR}/libs/config) SET(IPC_FOLDER ${PROJECT_SOURCE_DIR}/libs/ipc) +SET(LXCPP_FOLDER ${PROJECT_SOURCE_DIR}/libs/lxcpp) SET(CLIENT_FOLDER ${PROJECT_SOURCE_DIR}/client) SET(SERVER_FOLDER ${PROJECT_SOURCE_DIR}/server) SET(ZONE_SUPPORT_FOLDER ${PROJECT_SOURCE_DIR}/zone-support) @@ -180,6 +180,10 @@ IF(NOT DEFINED DATA_DIR) SET(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share") ENDIF(NOT DEFINED DATA_DIR) +IF(NOT DEFINED LIBEXEC_DIR) + SET(LIBEXEC_DIR "${CMAKE_INSTALL_PREFIX}/libexec") +ENDIF(NOT DEFINED LIBEXEC_DIR) + IF(NOT DEFINED RUN_DIR) SET(RUN_DIR "/var/run") ENDIF(NOT DEFINED RUN_DIR) @@ -190,12 +194,12 @@ SET(VSM_UNIT_TESTS_IPC_SOCKET_PATH ${RUN_DIR}/vasum-ipc-unit-tests.socket) ADD_SUBDIRECTORY(${COMMON_FOLDER}) ADD_SUBDIRECTORY(${LOGGER_FOLDER}) -ADD_SUBDIRECTORY(${LXCPP_FOLDER}) IF(NOT WITHOUT_DBUS) ADD_SUBDIRECTORY(${DBUS_FOLDER}) ENDIF(NOT WITHOUT_DBUS) ADD_SUBDIRECTORY(${CONFIG_FOLDER}) ADD_SUBDIRECTORY(${IPC_FOLDER}) +ADD_SUBDIRECTORY(${LXCPP_FOLDER}) ADD_SUBDIRECTORY(${CLIENT_FOLDER}) ADD_SUBDIRECTORY(${SERVER_FOLDER}) IF(NOT WITHOUT_DBUS) diff --git a/common/utils/channel.cpp b/common/utils/channel.cpp index fbb110d..3a0dd31 100644 --- a/common/utils/channel.cpp +++ b/common/utils/channel.cpp @@ -27,6 +27,8 @@ #include "logger/logger.hpp" +#include +#include #include namespace { @@ -36,10 +38,15 @@ const int RIGHT = 1; namespace utils { -Channel::Channel() +Channel::Channel(const bool closeOnExec) : mSocketIndex(-1) { - if (::socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, mSockets) < 0) { + int flags = SOCK_STREAM; + if (closeOnExec) { + flags |= SOCK_CLOEXEC; + }; + + if (::socketpair(AF_LOCAL, flags, 0, mSockets.data()) < 0) { const std::string msg = "socketpair() failed: " + utils::getSystemErrorMessage(); LOGE(msg); @@ -47,23 +54,36 @@ Channel::Channel() } } +Channel::Channel(const int fd) + : mSocketIndex(LEFT), + mSockets{{fd, -1}} +{ + assert(fd >= 0); +} + Channel::~Channel() { closeSocket(LEFT); closeSocket(RIGHT); } +/* + * This function has to be safe in regard to signal(7) + */ void Channel::setLeft() { mSocketIndex = LEFT; - utils::close(mSockets[RIGHT]); + ::close(mSockets[RIGHT]); mSockets[RIGHT] = -1; } +/* + * This function has to be safe in regard to signal(7) + */ void Channel::setRight() { mSocketIndex = RIGHT; - utils::close(mSockets[LEFT]); + ::close(mSockets[LEFT]); mSockets[LEFT] = -1; } @@ -73,6 +93,33 @@ void Channel::shutdown() closeSocket(mSocketIndex); } +int Channel::getFD() +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + return mSockets[mSocketIndex]; +} + +int Channel::getLeftFD() +{ + return mSockets[LEFT]; +} + +int Channel::getRightFD() +{ + return mSockets[RIGHT]; +} + +void Channel::setCloseOnExec(const bool closeOnExec) +{ + const int fd = getFD(); + + if (closeOnExec) { + ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC); + } else { + ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) & ~FD_CLOEXEC); + } +} + void Channel::closeSocket(int socketIndex) { utils::shutdown(mSockets[socketIndex]); diff --git a/common/utils/channel.hpp b/common/utils/channel.hpp index f537121..3b60daa 100644 --- a/common/utils/channel.hpp +++ b/common/utils/channel.hpp @@ -26,6 +26,8 @@ #define COMMON_UTILS_CHANNEL_HPP #include "utils/fd-utils.hpp" + +#include #include namespace utils { @@ -35,7 +37,8 @@ namespace utils { */ class Channel { public: - Channel(); + explicit Channel(const bool closeOnExec = true); + explicit Channel(const int fd); ~Channel(); Channel(const Channel&) = delete; @@ -72,12 +75,32 @@ public: template Data read(); + /** + * Get an active file descriptor + */ + int getFD(); + + /** + * Gen the left file descriptor + */ + int getLeftFD(); + + /** + * Gen the right file descriptor + */ + int getRightFD(); + + /** + * Sets close on exec on an active fd to either true or false + */ + void setCloseOnExec(const bool closeOnExec); + private: void closeSocket(int socketIndex); int mSocketIndex; - int mSockets[2]; + std::array mSockets; }; template diff --git a/libs/logger/backend-persistent-file.hpp b/libs/logger/backend-persistent-file.hpp index 74d2def..5cbd714 100644 --- a/libs/logger/backend-persistent-file.hpp +++ b/libs/logger/backend-persistent-file.hpp @@ -27,7 +27,9 @@ #include "logger/backend.hpp" +#include #include +#include namespace logger { @@ -35,7 +37,12 @@ class PersistentFileBackend : public LogBackend { public: PersistentFileBackend(const std::string& filePath) : mfilePath(filePath), - mOut(mfilePath, std::ios::app) {} + mOut(mfilePath, std::ios::app) + { + using filebufType = __gnu_cxx::stdio_filebuf; + const int fd = static_cast(mOut.rdbuf())->fd(); + ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC); + } void log(LogLevel logLevel, const std::string& file, diff --git a/libs/logger/formatter.cpp b/libs/logger/formatter.cpp index 17bed53..06404f2 100644 --- a/libs/logger/formatter.cpp +++ b/libs/logger/formatter.cpp @@ -26,6 +26,7 @@ #include "logger/formatter.hpp" #include "utils/ccolor.hpp" +#include #include #include #include @@ -39,7 +40,7 @@ namespace { const int TIME_COLUMN_LENGTH = 12; const int SEVERITY_COLUMN_LENGTH = 8; -const int THREAD_COLUMN_LENGTH = 3; +const int PROCESS_COLUMN_LENGTH = 8; const int FILE_COLUMN_LENGTH = 60; std::atomic gNextThreadId(1); @@ -122,7 +123,7 @@ std::string LogFormatter::getHeader(LogLevel logLevel, std::ostringstream logLine; logLine << getCurrentTime() << ' ' << std::left << std::setw(SEVERITY_COLUMN_LENGTH) << '[' + toString(logLevel) + ']' - << std::right << std::setw(THREAD_COLUMN_LENGTH) << getCurrentThread() << ": " + << std::right << std::setw(PROCESS_COLUMN_LENGTH) << ::getpid() << "/" << getCurrentThread() << ": " << std::left << std::setw(FILE_COLUMN_LENGTH) << file + ':' + std::to_string(line) + ' ' + func + ':'; return logLine.str(); diff --git a/libs/logger/level.hpp b/libs/logger/level.hpp index fd33343..99dc235 100644 --- a/libs/logger/level.hpp +++ b/libs/logger/level.hpp @@ -33,7 +33,7 @@ namespace logger { * @brief Available log levels * @ingroup libLogger */ -enum class LogLevel { +enum class LogLevel : int { TRACE, ///< Most detailed log level DEBUG, ///< Debug logs INFO, ///< Information diff --git a/libs/logger/logger.cpp b/libs/logger/logger.cpp index ec0855b..fbb85d6 100644 --- a/libs/logger/logger.cpp +++ b/libs/logger/logger.cpp @@ -40,6 +40,44 @@ std::mutex gLogMutex; } // namespace +void setupLogger(const LogType type, + const LogLevel level, + const std::string &arg) +{ + if (type == LogType::LOG_FILE || type == LogType::LOG_PERSISTENT_FILE) { + if (arg.empty()) { + throw std::runtime_error("Path needs to be specified in the agument"); + } + } + + switch(type) { + case LogType::LOG_NULL: + Logger::setLogBackend(new NullLogger()); + break; +#ifdef HAVE_SYSTEMD + case LogType::LOG_JOURNALD: + Logger::setLogBackend(new SystemdJournalBackend()); + break; +#endif + case LogType::LOG_FILE: + Logger::setLogBackend(new FileBackend(arg)); + break; + case LogType::LOG_PERSISTENT_FILE: + Logger::setLogBackend(new PersistentFileBackend(arg)); + break; + case LogType::LOG_SYSLOG: + Logger::setLogBackend(new SyslogBackend()); + break; + case LogType::LOG_STDERR: + Logger::setLogBackend(new StderrBackend()); + break; + default: + throw std::runtime_error("Bad logger type passed"); + } + + Logger::setLogLevel(level); +} + void Logger::logMessage(LogLevel logLevel, const std::string& message, const std::string& file, diff --git a/libs/logger/logger.hpp b/libs/logger/logger.hpp index 0a1ebe6..92baee1 100644 --- a/libs/logger/logger.hpp +++ b/libs/logger/logger.hpp @@ -61,9 +61,14 @@ #define COMMON_LOGGER_LOGGER_HPP #include "logger/level.hpp" +#include "logger/backend-null.hpp" +#ifdef HAVE_SYSTEMD +#include "logger/backend-journal.hpp" +#endif #include "logger/backend-file.hpp" -#include "logger/backend-stderr.hpp" #include "logger/backend-persistent-file.hpp" +#include "logger/backend-syslog.hpp" +#include "logger/backend-stderr.hpp" #include #include @@ -74,6 +79,27 @@ namespace logger { +enum class LogType : int { + LOG_NULL, + LOG_JOURNALD, + LOG_FILE, + LOG_PERSISTENT_FILE, + LOG_SYSLOG, + LOG_STDERR +}; + +/** + * A helper function to easily and completely setup a new logger + * + * @param type logger type to be set up + * @param level maximum log level that will be logged + * @param arg an argument used by some loggers, specific to them + * (e.g. file name for file loggers) + */ +void setupLogger(const LogType type, + const LogLevel level, + const std::string &arg = ""); + class LogBackend; class Logger { diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 203ddd3..8f391e8 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -19,6 +19,9 @@ PROJECT(lxcpp) +SET(GUARD_CODENAME "${PROJECT_NAME}-guard") +ADD_SUBDIRECTORY(guard) + MESSAGE(STATUS "") MESSAGE(STATUS "Generating makefile for the liblxcpp...") FILE(GLOB HEADERS *.hpp) @@ -37,15 +40,18 @@ ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_COMMANDS}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${_LIB_SOVERSION_} VERSION ${_LIB_VERSION_} + COMPILE_DEFINITIONS GUARD_PATH="${LIBEXEC_DIR}/${GUARD_CODENAME}" ) ADD_DEPENDENCIES(${PROJECT_NAME} Common Logger) ## Link libraries ############################################################## FIND_PACKAGE(Boost COMPONENTS system filesystem) +PKG_CHECK_MODULES(LXCPP_DEPS REQUIRED glib-2.0) + INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) -INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger) +INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS} ${LXCPP_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger Config) ## Generate the pc file ######################################################## CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY) diff --git a/libs/lxcpp/commands/attach.cpp b/libs/lxcpp/commands/attach.cpp index 01a811b..419f651 100644 --- a/libs/lxcpp/commands/attach.cpp +++ b/libs/lxcpp/commands/attach.cpp @@ -167,6 +167,8 @@ void Attach::execute() parent(intermChannel, interPid); intermChannel.shutdown(); } else { + // TODO: only safe functions mentioned in signal(7) should be used below. + // This is not the case now, needs fixing. intermChannel.setRight(); interm(intermChannel, call); intermChannel.shutdown(); diff --git a/libs/lxcpp/commands/start.cpp b/libs/lxcpp/commands/start.cpp new file mode 100644 index 0000000..f68654a --- /dev/null +++ b/libs/lxcpp/commands/start.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief Implementation of starting a container + */ + +#include "lxcpp/commands/start.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" +#include "lxcpp/utils.hpp" + +#include "logger/logger.hpp" +#include "config/manager.hpp" + +#include + + +namespace lxcpp { + + +Start::Start(ContainerConfig &config) + : mConfig(config), + mChannel(false), + mGuardPath(GUARD_PATH), + mChannelFD(std::to_string(mChannel.getRightFD())) +{ +} + +Start::~Start() +{ +} + +void Start::execute() +{ + LOGD("Forking daemonize and guard processes. Execing guard libexec binary."); + LOGD("Logging will cease now. It should be restored using some new facility in the guard process."); + const pid_t pid = lxcpp::fork(); + + if (pid > 0) { + mChannel.setLeft(); + parent(pid); + } else { + // Below this point only safe functions mentioned in signal(7) are allowed. + mChannel.setRight(); + daemonize(); + ::_exit(EXIT_FAILURE); + } +} + +void Start::parent(const pid_t pid) +{ + int status = lxcpp::waitpid(pid); + if (status != EXIT_SUCCESS) { + const std::string msg = "Problem with a daemonize process"; + LOGE(msg); + throw ProcessSetupException(msg); + } + + // Send the config to the guard process + config::saveToFD(mChannel.getFD(), mConfig); + + // Read the pids from the guard process + mConfig.mGuardPid = mChannel.read(); + mConfig.mInitPid = mChannel.read(); + + mChannel.shutdown(); + + if (mConfig.mGuardPid <= 0 || mConfig.mInitPid <= 0) { + const std::string msg = "Problem with receiving pids"; + LOGE(msg); + throw ProcessSetupException(msg); + } + + LOGD("Guard PID: " << mConfig.mGuardPid); + LOGD("Init PID: " << mConfig.mInitPid); +} + +void Start::daemonize() +{ + // Double fork() with exit() to reattach the process under the host's init + pid_t pid = ::fork(); + if (pid < 0) { + ::_exit(EXIT_FAILURE); + } + + if (pid == 0) { + // Prepare a clean environment for a guard process: + // - chdir to / so it's independent on other directories + // - null std* fds so it's properly dettached from the terminal + // - set a new session so it cannot reacquire a terminal + + if (::chdir("/") < 0) { + ::_exit(EXIT_FAILURE); + } + if (nullStdFDs() <0) { + ::_exit(EXIT_FAILURE); + } + if (::setsid() < 0) { + ::_exit(EXIT_FAILURE); + } + + // Add name and path of the container to argv. They are not used, but will + // identify the container in the process list in case setProcTitle() fails + // and will guarantee we have enough argv memory to write the title we want. + const char *argv[] = {mGuardPath.c_str(), + mChannelFD.c_str(), + mConfig.mName.c_str(), + mConfig.mRootPath.c_str(), + NULL}; + ::execve(argv[0], const_cast(argv), NULL); + ::_exit(EXIT_FAILURE); + } + + ::_exit(EXIT_SUCCESS); +} + + +} // namespace lxcpp diff --git a/libs/lxcpp/commands/start.hpp b/libs/lxcpp/commands/start.hpp new file mode 100644 index 0000000..758b471 --- /dev/null +++ b/libs/lxcpp/commands/start.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief Headers for starting a container + */ + +#ifndef LXCPP_COMMANDS_START_HPP +#define LXCPP_COMMANDS_START_HPP + +#include "lxcpp/commands/command.hpp" +#include "lxcpp/container-config.hpp" +#include "utils/channel.hpp" + +#include + + +namespace lxcpp { + + +class Start final: Command { +public: + /** + * Starts the container + * + * In more details it prepares an environment for a guard process, + * starts it, and passes it the configuration through a file descriptor. + * + * @param config container's config + */ + Start(ContainerConfig &config); + ~Start(); + + void execute(); + +private: + ContainerConfig &mConfig; + utils::Channel mChannel; + std::string mGuardPath; + std::string mChannelFD; + + void parent(const pid_t pid); + void daemonize(); +}; + + +} // namespace lxcpp + + +#endif // LXCPP_COMMANDS_START_HPP diff --git a/libs/lxcpp/container-config.hpp b/libs/lxcpp/container-config.hpp new file mode 100644 index 0000000..9caad96 --- /dev/null +++ b/libs/lxcpp/container-config.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief A definition of a ContainerConfig struct + */ + +#ifndef LXCPP_CONTAINER_CONFIG_HPP +#define LXCPP_CONTAINER_CONFIG_HPP + +#include "lxcpp/logger-config.hpp" + +#include +#include + +#include +#include +#include + + +namespace lxcpp { + + +struct ContainerConfig { + /** + * Name of the container. + * + * Set: by constructor, cannot be changed afterwards. + * Get: getName() + */ + std::string mName; + + /** + * Path of the root directory of the container. + * + * Set: by contstructor, cannot be changed afterwards. + * Get: getRootPath() + */ + std::string mRootPath; + + /** + * Pid of the guard process. + * + * Set: automatically by the guard process itself. + * Get: getGuardPid() + */ + pid_t mGuardPid; + + /** + * Pid of the container's init process. + * + * Set: automatically by the guard process. + * Get: getInitPid() + */ + pid_t mInitPid; + + /** + * Argv of the container's init process to be executed. + * The path has to be relative to the RootPath. + * + * Set: setInit() + * Get: getInit() + */ + std::vector mInit; + + /** + * Logger to be configured inside the guard process. This logger + * reconfiguration is due to the fact that guard looses standard file + * descriptors and might loose access to other files by mount namespace + * usage. Hence an option to set some other logger that will work + * regardless. E.g. PersistentFile. + * + * Set: setLogger() + * Get: none + */ + LoggerConfig mLogger; + + ContainerConfig() : mGuardPid(-1), mInitPid(-1) {} + + CONFIG_REGISTER + ( + mName, + mRootPath, + mGuardPid, + mInitPid, + mInit, + mLogger + ) +}; + + +} + + +#endif // LXCPP_CONTAINER_CONFIG_HPP diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 8249009..cee6fa4 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -28,6 +28,7 @@ #include "lxcpp/namespace.hpp" #include "lxcpp/capability.hpp" #include "lxcpp/commands/attach.hpp" +#include "lxcpp/commands/start.hpp" #include "logger/logger.hpp" #include "utils/exception.hpp" @@ -120,9 +121,18 @@ pid_t ContainerImpl::getInitPid() const return mConfig.mInitPid; } +void ContainerImpl::setLogger(const logger::LogType type, + const logger::LogLevel level, + const std::string &arg) +{ + mConfig.mLogger.set(type, level, arg); +} + void ContainerImpl::start() { - throw NotImplementedException(); + // TODO: check config consistency and completeness somehow + Start start(mConfig); + start.execute(); } void ContainerImpl::stop() diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index 5435d49..1bf44b7 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -25,37 +25,15 @@ #define LXCPP_CONTAINER_IMPL_HPP #include -#include -#include #include +#include "lxcpp/container-config.hpp" #include "lxcpp/container.hpp" #include "lxcpp/namespace.hpp" #include "lxcpp/network.hpp" -#include "utils/channel.hpp" - namespace lxcpp { -struct ContainerConfig { - std::string mName; - std::string mRootPath; - pid_t mGuardPid; - pid_t mInitPid; - std::vector mInit; - - ContainerConfig() : mGuardPid(-1), mInitPid(-1) {} - - CONFIG_REGISTER - ( - mName, - mRootPath, - mGuardPid, - mInitPid, - mInit - ) -}; - class ContainerImpl : public virtual Container { public: @@ -73,6 +51,10 @@ public: const std::vector& getInit(); void setInit(const std::vector &init); + void setLogger(const logger::LogType type, + const logger::LogLevel level, + const std::string &arg); + const std::vector& getNamespaces() const; // Execution actions diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 192f2df..88c3ff8 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -25,6 +25,7 @@ #define LXCPP_CONTAINER_HPP #include "lxcpp/network-config.hpp" +#include "lxcpp/logger-config.hpp" #include #include @@ -60,6 +61,10 @@ public: virtual const std::vector& getInit() = 0; virtual void setInit(const std::vector &init) = 0; + virtual void setLogger(const logger::LogType type, + const logger::LogLevel level, + const std::string &arg) = 0; + // Execution actions virtual void start() = 0; virtual void stop() = 0; diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 7764282..11a6a0e 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -66,6 +66,11 @@ struct CapabilitySetupException: public Exception { : Exception(message) {} }; +struct UtilityException: public Exception { + explicit UtilityException(const std::string& message = "Error during an utility operation") + : Exception(message) {} +}; + struct BadArgument: public Exception { explicit BadArgument(const std::string& message = "Bad argument passed") : Exception(message) {} diff --git a/libs/lxcpp/guard/CMakeLists.txt b/libs/lxcpp/guard/CMakeLists.txt new file mode 100644 index 0000000..6c69300 --- /dev/null +++ b/libs/lxcpp/guard/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1 as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# @file CMakeLists.txt +# @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) +# + +MESSAGE(STATUS "") +MESSAGE(STATUS "Generating makefile for the Guard...") +FILE(GLOB guard_SRCS *.cpp *.hpp) + + +## Setup target ################################################################ +ADD_EXECUTABLE(${GUARD_CODENAME} ${guard_SRCS}) + + +## Link libraries ############################################################## +PKG_CHECK_MODULES(GUARD_DEPS REQUIRED glib-2.0) +INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) +INCLUDE_DIRECTORIES(SYSTEM ${GUARD_DEPS_INCLUDE_DIRS} ${JSON_C_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(${GUARD_CODENAME} lxcpp) + + +## Install ##################################################################### +INSTALL(TARGETS ${GUARD_CODENAME} DESTINATION ${LIBEXEC_DIR}) diff --git a/libs/lxcpp/guard/guard.cpp b/libs/lxcpp/guard/guard.cpp new file mode 100644 index 0000000..63c7263 --- /dev/null +++ b/libs/lxcpp/guard/guard.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief LXCPP guard process implementation + */ + +#include "lxcpp/utils.hpp" +#include "lxcpp/guard/guard.hpp" +#include "lxcpp/process.hpp" + +#include "config/manager.hpp" +#include "logger/logger.hpp" + +#include +#include + + +namespace lxcpp { + + +void startContainer(const ContainerConfig &cfg) +{ + std::vector argv; + argv.reserve(cfg.mInit.size() + 1); + for (auto const & it : cfg.mInit) { + argv.push_back(it.c_str()); + } + argv.push_back(nullptr); + + LOGD("Executing container's init: " << argv[0]); + ::execve(argv[0], const_cast(argv.data()), NULL); + ::_exit(EXIT_FAILURE); +} + +int startGuard(int channelFD) +{ + ContainerConfig cfg; + utils::Channel channel(channelFD); + channel.setCloseOnExec(true); + config::loadFromFD(channel.getFD(), cfg); + + logger::setupLogger(cfg.mLogger.getType(), + cfg.mLogger.getLevel(), + cfg.mLogger.getArg()); + + LOGD("Guard started, config & logging restored"); + + try { + LOGD("Setting the guard process title"); + const std::string title = "[LXCPP] " + cfg.mName + " " + cfg.mRootPath; + setProcTitle(title); + } catch (std::exception &e) { + // Ignore, this is optional + LOGW("Failed to set the guard process title"); + } + + // TODO: container preparation part 1 + + // TODO: switch to clone + LOGD("Forking container's init process"); + pid_t pid = lxcpp::fork(); + + if (pid == 0) { + // TODO: container preparation part 2 + + startContainer(cfg); + ::_exit(EXIT_FAILURE); + } + + cfg.mGuardPid = ::getpid(); + cfg.mInitPid = pid; + + channel.write(cfg.mGuardPid); + channel.write(cfg.mInitPid); + channel.shutdown(); + + int status = lxcpp::waitpid(pid); + LOGD("Init exited with status: " << status); + return status; +} + + +} // namespace lxcpp diff --git a/libs/lxcpp/guard/guard.hpp b/libs/lxcpp/guard/guard.hpp new file mode 100644 index 0000000..78457ae --- /dev/null +++ b/libs/lxcpp/guard/guard.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief LXCPP guard process header + */ + +#ifndef LXCPP_GUARD_GUARD_HPP +#define LXCPP_GUARD_GUARD_HPP + + +#include "lxcpp/container-config.hpp" +#include "utils/channel.hpp" + +namespace lxcpp { + + +int startGuard(int channelFD); + + +} // namespace lxcpp + +#endif // LXCPP_GUARD_HPP diff --git a/libs/lxcpp/guard/main.cpp b/libs/lxcpp/guard/main.cpp new file mode 100644 index 0000000..c8db30b --- /dev/null +++ b/libs/lxcpp/guard/main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief Main file for the Guard process (libexec) + */ + +#include "lxcpp/guard/guard.hpp" + +#include "utils/fd-utils.hpp" + +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc == 1) { + std::cout << "This file should not be executed by hand" << std::endl; + ::_exit(EXIT_FAILURE); + } + + // NOTE: this might not be required now, but I leave it here not to forget. + // We need to investigate this with vasum and think about possibility of + // poorly written software that leaks file descriptors and might use LXCPP. +#if 0 + for(int fd = 3; fd < ::sysconf(_SC_OPEN_MAX); ++fd) { + utils::close(fd); + } +#endif + + int fd = std::stoi(argv[1]); + return lxcpp::startGuard(fd); +} diff --git a/libs/lxcpp/logger-config.cpp b/libs/lxcpp/logger-config.cpp new file mode 100644 index 0000000..3aed302 --- /dev/null +++ b/libs/lxcpp/logger-config.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsumg.com) + * @brief Logger configuration + */ + +#include "lxcpp/logger-config.hpp" +#include "lxcpp/exception.hpp" + +namespace lxcpp { + + +void LoggerConfig::set(const logger::LogType type, + const logger::LogLevel level, + const std::string &arg) +{ + if (type == logger::LogType::LOG_FILE || type == logger::LogType::LOG_PERSISTENT_FILE) { + if (arg.empty()) { + const std::string msg = "Path needs to be specified in the agument"; + LOGE(msg); + throw BadArgument(msg); + } + } + + mType = static_cast(type); + mLevel = static_cast(level); + mArg = arg; +} + + +} //namespace lxcpp diff --git a/libs/lxcpp/logger-config.hpp b/libs/lxcpp/logger-config.hpp new file mode 100644 index 0000000..8312623 --- /dev/null +++ b/libs/lxcpp/logger-config.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsumg.com) + * @brief Logger configuration + */ + +#ifndef LXCPP_LOGGER_CONFIG_HPP +#define LXCPP_LOGGER_CONFIG_HPP + +#include "config/config.hpp" +#include "config/fields.hpp" +#include "logger/logger.hpp" + + +namespace lxcpp { + + +/** + * Logger configuration + */ +struct LoggerConfig { +private: + int mType; + int mLevel; + std::string mArg; + +public: + void set(const logger::LogType type, + const logger::LogLevel level, + const std::string &arg = ""); + + logger::LogType getType() const {return static_cast(mType);} + logger::LogLevel getLevel() const {return static_cast(mLevel);} + std::string getArg() const {return mArg;} + + CONFIG_REGISTER + ( + mType, + mLevel, + mArg + ) +}; + + +} //namespace lxcpp + + +#endif // LXCPP_LOGGER_CONFIG_HPP diff --git a/libs/lxcpp/utils.cpp b/libs/lxcpp/utils.cpp new file mode 100644 index 0000000..41e76b5 --- /dev/null +++ b/libs/lxcpp/utils.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief LXCPP utils implementation + */ + +#include "lxcpp/exception.hpp" + +#include "logger/logger.hpp" +#include "utils/fd-utils.hpp" +#include "utils/exception.hpp" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + + +namespace lxcpp { + + +/* + * This function has to be safe in regard to signal(7) + */ +int nullStdFDs() +{ + int ret = -1; + + int fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR | O_CLOEXEC)); + if (fd == -1) { + goto err; + } + + if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDIN_FILENO))) { + goto err_close; + } + + if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDOUT_FILENO))) { + goto err_close; + } + + if (-1 == TEMP_FAILURE_RETRY(::dup2(fd, STDERR_FILENO))) { + goto err_close; + } + + if (-1 == TEMP_FAILURE_RETRY(::close(fd))) { + goto err_close; + } + + return 0; + +err_close: + TEMP_FAILURE_RETRY(::close(fd)); +err: + return ret; +} + +void setProcTitle(const std::string &title) +{ + std::ifstream f("/proc/self/stat"); + auto it = std::istream_iterator(f); + + // Skip the first 47 fields, entries 48-49 are ARG_START and ARG_END. + std::advance(it, 47); + unsigned long argStart = std::stol(*it++); + unsigned long argEnd = std::stol(*it++); + + f.close(); + + char *mem = reinterpret_cast(argStart); + ptrdiff_t oldLen = argEnd - argStart; + + // Include the null byte here, because in the calculations below we want to have room for it. + size_t newLen = title.length() + 1; + + // We shouldn't use more then we have available. Hopefully that should be enough. + if ((long)newLen > oldLen) { + newLen = oldLen; + } else { + argEnd = argStart + newLen; + } + + // Sanity check + if (argEnd < newLen || argEnd < argStart) { + std::string msg = "setProcTitle() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw UtilityException(msg); + } + + // Let's try to set the memory range properly (this requires capabilities) + if (::prctl(PR_SET_MM, PR_SET_MM_ARG_END, argEnd, 0, 0) < 0) { + // If that failed let's fall back to the poor man's version, just zero the memory we already have. + ::bzero(mem, oldLen); + } + + ::strcpy(mem, title.c_str()); +} + + +} // namespace lxcpp diff --git a/libs/lxcpp/utils.hpp b/libs/lxcpp/utils.hpp new file mode 100644 index 0000000..2a59303 --- /dev/null +++ b/libs/lxcpp/utils.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @brief LXCPP utils headers + */ + +#ifndef LXCPP_UTILS_HPP +#define LXCPP_UTILS_HPP + +#include + + +namespace lxcpp { + +/** + * Nullifies all standard file descriptors (stdin, stdout, stderr) + * replacing them with file descriptor to /dev/null. Used to + * as a part of a process to detach a process from a control terminal. + * + * This function has to be safe in regard to signal(7) + * + * @returns an error code in case of failure. + */ +int nullStdFDs(); + +/** + * Changes the tittle of a current process title (e.g. visible in ps tool). + * + * @param title A new tittle to be set + */ +void setProcTitle(const std::string &title); + + +} // namespace lxcpp + + +#endif // LXCPP_START_HPP diff --git a/packaging/vasum.spec b/packaging/vasum.spec index 2cd5dd8..7709ab4 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -487,6 +487,7 @@ The package provides liblxcpp library. %files -n liblxcpp %defattr(644,root,root,755) %{_libdir}/liblxcpp.so.0 +%attr(755,root,root) %{_libexecdir}/lxcpp-guard %attr(755,root,root) %{_libdir}/liblxcpp.so.%{version} %package -n liblxcpp-devel