[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
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)
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)
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)
#include "logger/logger.hpp"
+#include <fcntl.h>
+#include <unistd.h>
#include <sys/socket.h>
namespace {
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);
}
}
+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;
}
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]);
#define COMMON_UTILS_CHANNEL_HPP
#include "utils/fd-utils.hpp"
+
+#include <array>
#include <cassert>
namespace utils {
*/
class Channel {
public:
- Channel();
+ explicit Channel(const bool closeOnExec = true);
+ explicit Channel(const int fd);
~Channel();
Channel(const Channel&) = delete;
template<typename Data>
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<int, 2> mSockets;
};
template<typename Data>
#include "logger/backend.hpp"
+#include <fcntl.h>
#include <fstream>
+#include <ext/stdio_filebuf.h>
namespace logger {
public:
PersistentFileBackend(const std::string& filePath) :
mfilePath(filePath),
- mOut(mfilePath, std::ios::app) {}
+ mOut(mfilePath, std::ios::app)
+ {
+ using filebufType = __gnu_cxx::stdio_filebuf<std::ofstream::char_type>;
+ const int fd = static_cast<filebufType*>(mOut.rdbuf())->fd();
+ ::fcntl(fd, F_SETFD, ::fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ }
void log(LogLevel logLevel,
const std::string& file,
#include "logger/formatter.hpp"
#include "utils/ccolor.hpp"
+#include <unistd.h>
#include <sys/time.h>
#include <cassert>
#include <sstream>
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<unsigned int> gNextThreadId(1);
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();
* @brief Available log levels
* @ingroup libLogger
*/
-enum class LogLevel {
+enum class LogLevel : int {
TRACE, ///< Most detailed log level
DEBUG, ///< Debug logs
INFO, ///< Information
} // 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,
#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 <sstream>
#include <string>
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 {
PROJECT(lxcpp)
+SET(GUARD_CODENAME "${PROJECT_NAME}-guard")
+ADD_SUBDIRECTORY(guard)
+
MESSAGE(STATUS "")
MESSAGE(STATUS "Generating makefile for the liblxcpp...")
FILE(GLOB HEADERS *.hpp)
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)
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();
--- /dev/null
+/*
+ * 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 <unistd.h>
+
+
+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<pid_t>();
+ mConfig.mInitPid = mChannel.read<pid_t>();
+
+ 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<char *const*>(argv), NULL);
+ ::_exit(EXIT_FAILURE);
+ }
+
+ ::_exit(EXIT_SUCCESS);
+}
+
+
+} // namespace lxcpp
--- /dev/null
+/*
+ * 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 <sys/types.h>
+
+
+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
--- /dev/null
+/*
+ * 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 <config/config.hpp>
+#include <config/fields.hpp>
+
+#include <string>
+#include <vector>
+#include <sys/types.h>
+
+
+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<std::string> 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
#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"
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()
#define LXCPP_CONTAINER_IMPL_HPP
#include <sys/types.h>
-#include <config/config.hpp>
-#include <config/fields.hpp>
#include <memory>
+#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<std::string> mInit;
-
- ContainerConfig() : mGuardPid(-1), mInitPid(-1) {}
-
- CONFIG_REGISTER
- (
- mName,
- mRootPath,
- mGuardPid,
- mInitPid,
- mInit
- )
-};
-
class ContainerImpl : public virtual Container {
public:
const std::vector<std::string>& getInit();
void setInit(const std::vector<std::string> &init);
+ void setLogger(const logger::LogType type,
+ const logger::LogLevel level,
+ const std::string &arg);
+
const std::vector<Namespace>& getNamespaces() const;
// Execution actions
#define LXCPP_CONTAINER_HPP
#include "lxcpp/network-config.hpp"
+#include "lxcpp/logger-config.hpp"
#include <sys/types.h>
#include <string>
virtual const std::vector<std::string>& getInit() = 0;
virtual void setInit(const std::vector<std::string> &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;
: 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) {}
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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 <unistd.h>
+#include <sys/wait.h>
+
+
+namespace lxcpp {
+
+
+void startContainer(const ContainerConfig &cfg)
+{
+ std::vector<char const *> 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<char *const*>(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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <iostream>
+#include <unistd.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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<int>(type);
+ mLevel = static_cast<int>(level);
+ mArg = arg;
+}
+
+
+} //namespace lxcpp
--- /dev/null
+/*
+ * 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<logger::LogType>(mType);}
+ logger::LogLevel getLevel() const {return static_cast<logger::LogLevel>(mLevel);}
+ std::string getArg() const {return mArg;}
+
+ CONFIG_REGISTER
+ (
+ mType,
+ mLevel,
+ mArg
+ )
+};
+
+
+} //namespace lxcpp
+
+
+#endif // LXCPP_LOGGER_CONFIG_HPP
--- /dev/null
+/*
+ * 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 <iostream>
+#include <iterator>
+#include <unistd.h>
+#include <string.h>
+#include <sys/prctl.h>
+
+
+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<std::string>(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<char*>(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
--- /dev/null
+/*
+ * 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 <string>
+
+
+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
%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