* @brief File descriptor utility functions
*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#include "config.hpp"
#include "utils/fd-utils.hpp"
#include <cstring>
#include <chrono>
#include <unistd.h>
+#include <fcntl.h>
#include <poll.h>
#include <sys/resource.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
} // namespace
+
+int open(const std::string &path, int flags, mode_t mode)
+{
+ assert(!(flags & O_CREAT || flags & O_TMPFILE) || mode >= 0);
+
+ int fd;
+
+ for (;;) {
+ fd = ::open(path.c_str(), flags, mode);
+
+ if (-1 == fd) {
+ if (errno == EINTR) {
+ LOGT("open() interrupted by a signal, retrying");
+ continue;
+ }
+ const std::string msg = "open() failed: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+ break;
+ }
+
+ return fd;
+}
+
void close(int fd)
{
if (fd < 0) {
for (;;) {
if (-1 == ::close(fd)) {
if (errno == EINTR) {
- LOGT("Close interrupted by a signal, retrying");
+ LOGT("close() interrupted by a signal, retrying");
continue;
}
LOGE("Error in close: " << getSystemErrorMessage());
}
}
+int ioctl(int fd, unsigned long request, void *argp)
+{
+ int ret = ::ioctl(fd, request, argp);
+ if (ret == -1) {
+ const std::string msg = "ioctl() failed: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+ return ret;
+}
+
+int dup2(int oldFD, int newFD, bool closeOnExec)
+{
+ int flags = 0;
+ if (closeOnExec) {
+ flags |= O_CLOEXEC;
+ }
+ int fd = dup3(oldFD, newFD, flags);
+ if (fd == -1) {
+ const std::string msg = "dup3() failed: " + getSystemErrorMessage();
+ LOGE(msg);
+ throw UtilsException(msg);
+ }
+ return fd;
+}
+
void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS)
{
chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() +
#ifndef COMMON_UTILS_FD_HPP
#define COMMON_UTILS_FD_HPP
+#include <string>
#include <cstddef>
+#include <sys/types.h>
namespace utils {
/**
+ * Open a file.
+ */
+int open(const std::string &path, int flags, mode_t mode = -1);
+
+/**
* Close the file descriptor.
*/
void close(int fd);
void shutdown(int fd);
/**
+ * Operation on a special file
+ */
+int ioctl(int fd, unsigned long request, void *argp);
+
+/**
+ * Duplicate one file desciptor onto another
+ */
+int dup2(int olfFD, int newFD, bool closeOnExec = false);
+
+/**
* Write to a file descriptor, throw on error.
*
* @param fd file descriptor
mEnvToKeep(envToKeep),
mEnvToSet(envToSet)
{
- mTTYFD = ::open(ttyPath.c_str(), O_RDWR | O_NOCTTY);
- if (mTTYFD < 0) {
- const std::string msg = "open() failed: " +
- utils::getSystemErrorMessage();
- LOGE(msg);
- throw BadArgument(msg);
- }
+ mTTYFD = utils::open(ttyPath, O_RDWR | O_NOCTTY);
}
Attach::~Attach()
--- /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 guest terminal preparation
+ */
+
+#include "lxcpp/commands/prep-guest-terminal.hpp"
+#include "lxcpp/terminal.hpp"
+
+#include "utils/fd-utils.hpp"
+#include "logger/logger.hpp"
+
+
+namespace lxcpp {
+
+
+PrepGuestTerminal::PrepGuestTerminal(TerminalsConfig &terminals)
+ : mTerminals(terminals)
+{
+}
+
+PrepGuestTerminal::~PrepGuestTerminal()
+{
+}
+
+void PrepGuestTerminal::execute()
+{
+ LOGD("Preparing " << mTerminals.count << " pseudoterminal(s) on the guest side.");
+
+ // TODO when /dev/ is already namespaced, create:
+ // /dev/pts/x (N times) (or mount devpts?)
+ // symlink /dev/console -> first PTY
+ // symlink /dev/ttyY -> /dev/pts/X (N times)
+ //for (int i = 0; i < mTerminals.count; ++i) {}
+
+ // Setup first PTY as a controlling terminal (/dev/console).
+ // This way simple programs in the container can work
+ // and we will be able to see the output of a container's init
+ // before the launch of getty processes.
+ int fd = utils::open(mTerminals.PTYs[0].ptsName,
+ O_RDWR | O_CLOEXEC | O_NOCTTY);
+ setupIOControlTTY(fd);
+
+}
+
+
+} // 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 guest terminal preparation
+ */
+
+#ifndef LXCPP_COMMANDS_PREP_GUEST_TERMINAL_HPP
+#define LXCPP_COMMANDS_PREP_GUEST_TERMINAL_HPP
+
+#include "lxcpp/commands/command.hpp"
+#include "lxcpp/terminal-config.hpp"
+
+
+namespace lxcpp {
+
+
+class PrepGuestTerminal final: Command {
+public:
+ /**
+ * Prepares the terminal on the guest side.
+ *
+ * It fills the /dev/ directory of a container with appropriate
+ * entries representing the created PTYs. It also takes already
+ * created PTYs and sets the first one as a controlling terminal.
+ *
+ * @param config container's config
+ */
+ PrepGuestTerminal(TerminalsConfig &config);
+ ~PrepGuestTerminal();
+
+ void execute();
+
+private:
+ TerminalsConfig &mTerminals;
+};
+
+
+} // namespace lxcpp
+
+
+#endif // LXCPP_COMMANDS_PREP_GUEST_TERMINAL_HPP
{
LOGD("Creating " << mTerminals.count << " pseudoterminal(s) on the host side:");
- for (int i = 0; i < mTerminals.count; ++i)
- {
+ 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);
#include <mutex>
#include <algorithm>
+#include <ext/stdio_filebuf.h>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+
+
+namespace {
+
+// TODO: UGLY: REMOVEME:
+// It will be removed as soon as Container::console() will get implemented
+// I need it for now to know I didn't brake anything. It will be eradicated.
+void readTerminal(const lxcpp::TerminalConfig &term)
+{
+ char *buf = NULL;
+ size_t size = 0;
+ ssize_t ret;
+
+ printf("%s output:\n", term.ptsName.c_str());
+ usleep(10000);
+
+ FILE *fp = fdopen(term.masterFD.value, "r");
+ while((ret = getline(&buf, &size, fp)) != -1L) {
+ printf("%s", buf);
+ }
+ free(buf);
+}
+
+} // namespace
namespace lxcpp {
Start start(mConfig);
start.execute();
+
+ // TODO: UGLY: REMOVEME: read from 1st terminal
+ readTerminal(mConfig.mTerminals.PTYs[0]);
}
void ContainerImpl::stop()
{
+ // TODO: things to do when shuttting down the container:
+ // - close PTY master FDs from the config so we won't keep PTYs open
+
throw NotImplementedException();
}
}
}
+pid_t setsid()
+{
+ pid_t pid = ::setsid();
+ if (pid == -1) {
+ const std::string msg = "setsid() failed: " +
+ utils::getSystemErrorMessage();
+ LOGE(msg);
+ throw CredentialSetupException(msg);
+ }
+ return pid;
+}
+
} // namespace lxcpp
void setuid(const uid_t uid);
+pid_t setsid();
+
} // namespace lxcpp
-#endif // LXCPP_CREDENTIALS_HPP
\ No newline at end of file
+#endif // LXCPP_CREDENTIALS_HPP
#include "lxcpp/utils.hpp"
#include "lxcpp/guard/guard.hpp"
#include "lxcpp/process.hpp"
+#include "lxcpp/commands/prep-guest-terminal.hpp"
#include "config/manager.hpp"
#include "logger/logger.hpp"
if (pid == 0) {
// TODO: container preparation part 2
+ PrepGuestTerminal terminals(cfg.mTerminals);
+ terminals.execute();
+
startContainer(cfg);
::_exit(EXIT_FAILURE);
}
::_exit(EXIT_FAILURE);
}
- int channel = std::stoi(argv[1]);
+ int channelFD = 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) {
- if (fd != channel) {
+ if (fd != channelFD) {
utils::close(fd);
}
}
#endif
- return lxcpp::startGuard(channel);
+ return lxcpp::startGuard(channelFD);
}
void setns(const pid_t pid, const std::vector<Namespace>& namespaces)
{
- int dirFD = ::open(getNsPath(pid).c_str(), O_DIRECTORY | O_CLOEXEC);
- if(dirFD < 0) {
- const std::string msg = "open() failed: " +
- utils::getSystemErrorMessage();
- LOGE(msg);
- throw ProcessSetupException(msg);
- }
+ int dirFD = utils::open(getNsPath(pid), O_DIRECTORY | O_CLOEXEC);
// Open FDs connected with the requested namespaces
std::vector<int> fds(namespaces.size(), -1);
throw ProcessSetupException(msg);
}
}
-} // namespace lxcpp
\ No newline at end of file
+} // namespace lxcpp
*/
#include "lxcpp/exception.hpp"
+#include "lxcpp/credentials.hpp"
#include "logger/logger.hpp"
#include "utils/exception.hpp"
return ret;
}
+bool isatty(int fd)
+{
+ int ret = ::isatty(fd);
+ if (ret) {
+ return true;
+ }
+ if (errno == EINVAL || errno == ENOTTY) {
+ return false;
+ }
+
+ const std::string msg = "isatty() failed: " + utils::getSystemErrorMessage();
+ LOGE(msg);
+ throw TerminalException(msg);
+}
+
+void setupIOControlTTY(const int ttyFD)
+{
+ if (!lxcpp::isatty(ttyFD)) {
+ const std::string msg = "setupIOControlTTY(): file descriptor passed is not a terminal";
+ LOGE(msg);
+ throw TerminalException(msg);
+ }
+
+ lxcpp::setsid();
+ utils::ioctl(ttyFD, TIOCSCTTY, NULL);
+
+ utils::dup2(ttyFD, STDIN_FILENO);
+ utils::dup2(ttyFD, STDOUT_FILENO);
+ utils::dup2(ttyFD, STDERR_FILENO);
+}
+
std::pair<int, std::string> openPty(bool rawMode)
{
int master, slave;
int nullStdFDs();
/**
+ * Checks if a file descriptor is a terminal
+ */
+bool isatty(int fd);
+
+/**
+ * Setups the passed fd as a new control and IO (in, out, err) terminal
+ */
+void setupIOControlTTY(const int ttyFD);
+
+/**
* 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.