lxcpp: Terminal preparation part 2 (guest) 35/48535/2
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Fri, 18 Sep 2015 14:26:58 +0000 (16:26 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Tue, 22 Sep 2015 13:10:02 +0000 (15:10 +0200)
[Feature]       Make use of the created pseudoterminals in the container
[Verification]  Build, install, run tests

Changes in this commit:
- PrepGuestTerminal command
- it's main function: setupIOControlTTY()
- some more utils/terminal wrappers for syscalls
- long overdue open(2) wrapper

Change-Id: I235baec044ed82d3376cba3f2e5b4d18f5b1dd71

14 files changed:
common/utils/fd-utils.cpp
common/utils/fd-utils.hpp
libs/lxcpp/commands/attach.cpp
libs/lxcpp/commands/prep-guest-terminal.cpp [new file with mode: 0644]
libs/lxcpp/commands/prep-guest-terminal.hpp [new file with mode: 0644]
libs/lxcpp/commands/prep-host-terminal.cpp
libs/lxcpp/container-impl.cpp
libs/lxcpp/credentials.cpp
libs/lxcpp/credentials.hpp
libs/lxcpp/guard/guard.cpp
libs/lxcpp/guard/main.cpp
libs/lxcpp/process.cpp
libs/lxcpp/terminal.cpp
libs/lxcpp/terminal.hpp

index 3e0db3e..9bd7d29 100644 (file)
  * @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;
@@ -117,6 +123,31 @@ void setFDFlag(const int fd, const int getOp, const int setOp, const int flag, c
 
 } // 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) {
@@ -126,7 +157,7 @@ void close(int fd)
     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());
@@ -148,6 +179,32 @@ void shutdown(int fd)
     }
 }
 
+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() +
index 91bbff4..60aed45 100644 (file)
 #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);
@@ -40,6 +47,16 @@ 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
index 419f651..e1bdfef 100644 (file)
@@ -132,13 +132,7 @@ Attach::Attach(lxcpp::ContainerImpl& container,
       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()
diff --git a/libs/lxcpp/commands/prep-guest-terminal.cpp b/libs/lxcpp/commands/prep-guest-terminal.cpp
new file mode 100644 (file)
index 0000000..f496f86
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  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
diff --git a/libs/lxcpp/commands/prep-guest-terminal.hpp b/libs/lxcpp/commands/prep-guest-terminal.hpp
new file mode 100644 (file)
index 0000000..1086c3b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  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
index f219ff9..1dc4f3d 100644 (file)
@@ -43,8 +43,7 @@ void PrepHostTerminal::execute()
 {
     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);
index 715dad8..7b715f3 100644 (file)
 #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 {
 
@@ -149,10 +177,16 @@ void ContainerImpl::start()
 
     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();
 }
 
index 692be25..c127170 100644 (file)
@@ -62,5 +62,17 @@ void setuid(const uid_t uid)
     }
 }
 
+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
 
index ab1a490..aa56aca 100644 (file)
@@ -36,6 +36,8 @@ void setgid(const gid_t gid);
 
 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
index 63c7263..952cdf5 100644 (file)
@@ -24,6 +24,7 @@
 #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"
@@ -80,6 +81,9 @@ int startGuard(int channelFD)
     if (pid == 0) {
         // TODO: container preparation part 2
 
+        PrepGuestTerminal terminals(cfg.mTerminals);
+        terminals.execute();
+
         startContainer(cfg);
         ::_exit(EXIT_FAILURE);
     }
index 1065ecb..78c7476 100644 (file)
@@ -35,18 +35,18 @@ int main(int argc, char *argv[])
         ::_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);
 }
index 96475d5..0353d82 100644 (file)
@@ -79,13 +79,7 @@ pid_t clone(int (*function)(void *),
 
 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);
@@ -162,4 +156,4 @@ void unshare(const Namespace ns)
         throw ProcessSetupException(msg);
     }
 }
-} // namespace lxcpp
\ No newline at end of file
+} // namespace lxcpp
index ce77cd5..72c57af 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include "lxcpp/exception.hpp"
+#include "lxcpp/credentials.hpp"
 
 #include "logger/logger.hpp"
 #include "utils/exception.hpp"
@@ -119,6 +120,37 @@ err:
     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;
index 85ae6e2..421e4ce 100644 (file)
@@ -40,6 +40,16 @@ namespace lxcpp {
 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.