{
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);
- }
+ utils::setCloseOnExec(fd, closeOnExec);
}
void Channel::closeSocket(int socketIndex)
}
}
+void setFDFlag(const int fd, const int getOp, const int setOp, const int flag, const bool set)
+{
+ int ret = ::fcntl(fd, getOp);
+ if (ret == -1) {
+ std::string msg = "fcntl(): Failed to get FD flags: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+
+ if (set) {
+ ret = ::fcntl(fd, setOp, ret | flag);
+ } else {
+ ret = ::fcntl(fd, setOp, ret & ~flag);
+ }
+
+ if (ret == -1) {
+ std::string msg = "fcntl(): Failed to set FD flag: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+}
+
} // namespace
void close(int fd)
return true;
}
+void setCloseOnExec(int fd, bool closeOnExec)
+{
+ setFDFlag(fd, F_GETFD, F_SETFD, FD_CLOEXEC, closeOnExec);
+}
+
+void setNonBlocking(int fd, bool nonBlocking)
+{
+ setFDFlag(fd, F_GETFL, F_SETFL, O_NONBLOCK, nonBlocking);
+}
+
} // namespace utils
*/
int fdRecv(int socket, const unsigned int timeoutMS = 5000);
+/**
+ * Set or remove CLOEXEC on a file descriptor
+ */
+void setCloseOnExec(int fd, bool closeOnExec);
+
+/**
+ * Set or remove NONBLOCK on a file descriptor
+ */
+void setNonBlocking(int fd, bool nonBlocking);
+
} // namespace utils
#endif // COMMON_UTILS_FD_HPP
// Receive
for(;;) {
- ssize_t ret = ::recvmsg(mFD, &msgh, MSG_WAITALL);
+ ssize_t ret = ::recvmsg(mFD, &msgh, MSG_WAITALL | MSG_CMSG_CLOEXEC);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
// Neglected errors, retry
INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER})
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)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} Common ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger Config util)
## Generate the pc file ########################################################
CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY)
--- /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 host terminal preparation
+ */
+
+#include "lxcpp/commands/prep-host-terminal.hpp"
+#include "lxcpp/terminal.hpp"
+
+#include "logger/logger.hpp"
+
+
+namespace lxcpp {
+
+
+PrepHostTerminal::PrepHostTerminal(TerminalsConfig &terminals)
+ : mTerminals(terminals)
+{
+}
+
+PrepHostTerminal::~PrepHostTerminal()
+{
+}
+
+void PrepHostTerminal::execute()
+{
+ LOGD("Creating " << mTerminals.count << " pseudoterminal(s) on the host side:");
+
+ for (int i = 0; i < mTerminals.count; ++i)
+ {
+ const auto pty = lxcpp::openPty(true);
+ LOGD(pty.second << " has been created");
+ mTerminals.PTYs.emplace_back(pty.first, pty.second);
+ }
+}
+
+
+} // 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 host terminal preparation
+ */
+
+#ifndef LXCPP_COMMANDS_PREP_HOST_TERMINAL_HPP
+#define LXCPP_COMMANDS_PREP_HOST_TERMINAL_HPP
+
+#include "lxcpp/commands/command.hpp"
+#include "lxcpp/terminal-config.hpp"
+
+
+namespace lxcpp {
+
+
+class PrepHostTerminal final: Command {
+public:
+ /**
+ * Prepares the terminal on the host side.
+ *
+ * It creates a number of pseudoterminals and stores them
+ * to be passed to the guard and prepared for init process.
+ *
+ * @param config container's config
+ */
+ PrepHostTerminal(TerminalsConfig &config);
+ ~PrepHostTerminal();
+
+ void execute();
+
+private:
+ TerminalsConfig &mTerminals;
+};
+
+
+} // namespace lxcpp
+
+
+#endif // LXCPP_COMMANDS_PREP_HOST_TERMINAL_HPP
#include "lxcpp/exception.hpp"
#include "lxcpp/process.hpp"
#include "lxcpp/utils.hpp"
+#include "lxcpp/terminal.hpp"
#include "logger/logger.hpp"
#include "config/manager.hpp"
#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
namespace lxcpp {
void Start::daemonize()
{
+ // Prepare a clean daemonized environment for a guard process:
+
+ // Set a new session so the process looses its control terminal
+ if (::setsid() < 0) {
+ ::_exit(EXIT_FAILURE);
+ }
+
// Double fork() with exit() to reattach the process under the host's init
+ // and to make sure that the child (guard) is not a process group leader
+ // and cannot reacquire its control terminal
pid_t pid = ::fork();
- if (pid < 0) {
+ if (pid < 0) { // fork failed
+ ::_exit(EXIT_FAILURE);
+ }
+ if (pid > 0) { // exit in parent process
+ ::_exit(EXIT_SUCCESS);
+ }
+
+ // Chdir to / so it's independent on other directories
+ if (::chdir("/") < 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);
+ // Null std* fds so it's properly dettached from the terminal
+ if (nullStdFDs() < 0) {
::_exit(EXIT_FAILURE);
}
- ::_exit(EXIT_SUCCESS);
+ // 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);
}
#include "lxcpp/logger-config.hpp"
#include "lxcpp/network-config.hpp"
+#include "lxcpp/terminal-config.hpp"
#include <config/config.hpp>
#include <config/fields.hpp>
*/
LoggerConfig mLogger;
+ /**
+ * Configuration for terminal(s), from API point of view, only their number.
+ *
+ * Set: setTerminalCount()
+ * Get: none
+ */
+ TerminalsConfig mTerminals;
+
ContainerConfig() : mGuardPid(-1), mInitPid(-1) {}
CONFIG_REGISTER
mGuardPid,
mInitPid,
mInit,
- mLogger
+ mLogger,
+ mTerminals
)
};
#include "lxcpp/capability.hpp"
#include "lxcpp/commands/attach.hpp"
#include "lxcpp/commands/start.hpp"
+#include "lxcpp/commands/prep-host-terminal.hpp"
#include "logger/logger.hpp"
#include "utils/exception.hpp"
mConfig.mLogger.set(type, level, arg);
}
+void ContainerImpl::setTerminalCount(const unsigned int count)
+{
+ if (count == 0) {
+ const std::string msg = "Container needs at least one terminal";
+ LOGE(msg);
+ throw ConfigureException(msg);
+ }
+
+ mConfig.mTerminals.count = count;
+}
+
void ContainerImpl::start()
{
// TODO: check config consistency and completeness somehow
+
+ PrepHostTerminal terminal(mConfig.mTerminals);
+ terminal.execute();
+
Start start(mConfig);
start.execute();
}
const logger::LogLevel level,
const std::string &arg);
+ void setTerminalCount(const unsigned int count);
+
const std::vector<Namespace>& getNamespaces() const;
// Execution actions
const logger::LogLevel level,
const std::string &arg) = 0;
+ virtual void setTerminalCount(const unsigned int count) = 0;
+
// Execution actions
virtual void start() = 0;
virtual void stop() = 0;
: Exception(message) {}
};
+struct TerminalException: public Exception {
+ explicit TerminalException(const std::string& message = "Error during a terminal operation")
+ : Exception(message) {}
+};
+
struct BadArgument: public Exception {
explicit BadArgument(const std::string& message = "Bad argument passed")
: Exception(message) {}
::_exit(EXIT_FAILURE);
}
+ int channel = std::stoi(argv[1]);
+
// 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);
+ if (fd != channel) {
+ utils::close(fd);
+ }
}
#endif
- int fd = std::stoi(argv[1]);
- return lxcpp::startGuard(fd);
+ return lxcpp::startGuard(channel);
}
--- /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 Terminal configuration
+ */
+
+#ifndef LXCPP_TERMINAL_CONFIG_HPP
+#define LXCPP_TERMINAL_CONFIG_HPP
+
+#include "config/config.hpp"
+#include "config/fields.hpp"
+
+#include <vector>
+#include <string>
+
+
+namespace lxcpp {
+
+
+struct TerminalConfig {
+ config::FileDescriptor masterFD;
+ std::string ptsName;
+
+ TerminalConfig()
+ : masterFD(-1)
+ {}
+
+ TerminalConfig(const int masterFD, const std::string &ptsName)
+ : masterFD(masterFD),
+ ptsName(ptsName)
+ {}
+
+ CONFIG_REGISTER
+ (
+ masterFD,
+ ptsName
+ )
+};
+
+struct TerminalsConfig {
+ int count;
+ std::vector<TerminalConfig> PTYs;
+
+ TerminalsConfig(const int count = 1)
+ : count(count)
+ {}
+
+ CONFIG_REGISTER
+ (
+ count,
+ PTYs
+ )
+};
+
+
+} //namespace lxcpp
+
+
+#endif // LXCPP_TERMINAL_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 Terminal helpers implementation
+ */
+
+#include "lxcpp/exception.hpp"
+
+#include "logger/logger.hpp"
+#include "utils/exception.hpp"
+#include "utils/fd-utils.hpp"
+
+#include <string>
+#include <pty.h>
+#include <limits.h>
+#include <unistd.h>
+
+
+namespace lxcpp {
+
+
+namespace {
+
+void openpty(int *master, int *slave)
+{
+ // Do not use other parameters, they are not 100% safe
+ if (-1 == ::openpty(master, slave, NULL, NULL, NULL)) {
+ const std::string msg = "openpty() failed: " + utils::getSystemErrorMessage();
+ LOGE(msg);
+ throw TerminalException(msg);
+ }
+}
+
+void tcgetattr(const int fd, struct termios *termios_p)
+{
+ if (-1 == ::tcgetattr(fd, termios_p)) {
+ const std::string msg = "tcgetattr() failed: " + utils::getSystemErrorMessage();
+ LOGE(msg);
+ throw TerminalException(msg);
+ }
+}
+
+void tcsetattr(const int fd, const int optional_actions, const struct termios *termios_p)
+{
+ if (-1 == ::tcsetattr(fd, optional_actions, termios_p)) {
+ const std::string msg = "tcsetattr() failed: " + utils::getSystemErrorMessage();
+ LOGE(msg);
+ throw TerminalException(msg);
+ }
+}
+
+std::string ttyname_r(const int fd)
+{
+ char ptsName[PATH_MAX];
+ int rc = ::ttyname_r(fd, ptsName, PATH_MAX);
+
+ if (rc != 0) {
+ const std::string msg = "ttyname_r() failed: " + utils::getSystemErrorMessage(rc);
+ LOGE(msg);
+ throw TerminalException(msg);
+ }
+
+ return ptsName;
+}
+
+} // namespace
+
+
+/*
+ * 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;
+ }
+
+ return 0;
+
+err_close:
+ TEMP_FAILURE_RETRY(::close(fd));
+err:
+ return ret;
+}
+
+std::pair<int, std::string> openPty(bool rawMode)
+{
+ int master, slave;
+ std::string ptsName;
+
+ try {
+ lxcpp::openpty(&master, &slave);
+
+ utils::setCloseOnExec(master, true);
+ utils::setNonBlocking(master, true);
+
+ if (rawMode) {
+ struct termios ttyAttr;
+
+ lxcpp::tcgetattr(slave, &ttyAttr);
+ ::cfmakeraw(&ttyAttr);
+ lxcpp::tcsetattr(slave, TCSADRAIN, &ttyAttr);
+ }
+
+ ptsName = lxcpp::ttyname_r(slave);
+
+ } catch(std::exception&) {
+ TEMP_FAILURE_RETRY(::close(master));
+ TEMP_FAILURE_RETRY(::close(slave));
+
+ throw;
+ }
+
+ utils::close(slave);
+ return std::pair<int, std::string>(master, ptsName);
+}
+
+
+} // 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 Terminal helpers headers
+ */
+
+#ifndef LXCPP_TERMINAL_HPP
+#define LXCPP_TERMINAL_HPP
+
+
+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();
+
+/**
+ * This function creates a new pair of virtual character devices
+ * using a pseudtoreminal interface. It also configures as much as it
+ * can so the devices are immediately usable.
+ *
+ * @param rawMode Whether to set the terminal in the raw mode (termios(2))
+ *
+ * @returns file descriptor to the master device and the path/name of
+ * the pts slace device.
+ */
+std::pair<int, std::string> openPty(bool rawMode);
+
+
+} // namespace lxcpp
+
+
+
+#endif // LXCPP_TERMINAL_HPP
* @brief LXCPP utils implementation
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#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>
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");
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).