lxcpp: Terminal preparation part 1 (host) 70/48370/4
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Thu, 17 Sep 2015 16:18:04 +0000 (18:18 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Tue, 22 Sep 2015 11:22:58 +0000 (13:22 +0200)
[Feature]       Prepare pseudoterminals for the container
[Verification]  Build, install, run tests

Changes in this commit:
- PrepHostTerminal command and surrounding function (openPty)
- Terminal(s)Config
- Changes to Start::daemonize() to do its job better
- fd-utils helpers for FD flags
- set CLOEXEC on FDs received through libConfig

Change-Id: I432447900c189bb50669267ff4422c36860b5481

19 files changed:
common/utils/channel.cpp
common/utils/fd-utils.cpp
common/utils/fd-utils.hpp
libs/config/fdstore.cpp
libs/lxcpp/CMakeLists.txt
libs/lxcpp/commands/prep-host-terminal.cpp [new file with mode: 0644]
libs/lxcpp/commands/prep-host-terminal.hpp [new file with mode: 0644]
libs/lxcpp/commands/start.cpp
libs/lxcpp/container-config.hpp
libs/lxcpp/container-impl.cpp
libs/lxcpp/container-impl.hpp
libs/lxcpp/container.hpp
libs/lxcpp/exception.hpp
libs/lxcpp/guard/main.cpp
libs/lxcpp/terminal-config.hpp [new file with mode: 0644]
libs/lxcpp/terminal.cpp [new file with mode: 0644]
libs/lxcpp/terminal.hpp [new file with mode: 0644]
libs/lxcpp/utils.cpp
libs/lxcpp/utils.hpp

index 3a0dd31..52047ee 100644 (file)
@@ -113,11 +113,7 @@ 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);
-    }
+    utils::setCloseOnExec(fd, closeOnExec);
 }
 
 void Channel::closeSocket(int socketIndex)
index 3b7b998..3e0db3e 100644 (file)
@@ -93,6 +93,28 @@ void waitForEvent(int fd,
     }
 }
 
+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)
@@ -348,5 +370,15 @@ bool fdSend(int socket, int fd, const unsigned int timeoutMS)
     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
 
index 56e3f41..91bbff4 100644 (file)
@@ -86,6 +86,16 @@ bool fdSend(int socket, int fd, const unsigned int timeoutMS = 5000);
  */
 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
index cc0b903..44d2418 100644 (file)
@@ -262,7 +262,7 @@ int FDStore::receiveFD(const unsigned int timeoutMS)
 
     // 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
index 8f391e8..d5e205b 100644 (file)
@@ -51,7 +51,7 @@ PKG_CHECK_MODULES(LXCPP_DEPS REQUIRED glib-2.0)
 
 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)
diff --git a/libs/lxcpp/commands/prep-host-terminal.cpp b/libs/lxcpp/commands/prep-host-terminal.cpp
new file mode 100644 (file)
index 0000000..f219ff9
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  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
diff --git a/libs/lxcpp/commands/prep-host-terminal.hpp b/libs/lxcpp/commands/prep-host-terminal.hpp
new file mode 100644 (file)
index 0000000..f7153b2
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  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
index f68654a..32a3054 100644 (file)
 #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 {
@@ -94,41 +97,44 @@ void Start::parent(const pid_t pid)
 
 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);
 }
 
 
index 54422bf..c84b4ef 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "lxcpp/logger-config.hpp"
 #include "lxcpp/network-config.hpp"
+#include "lxcpp/terminal-config.hpp"
 
 #include <config/config.hpp>
 #include <config/fields.hpp>
@@ -100,6 +101,14 @@ struct ContainerConfig {
      */
     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
@@ -109,7 +118,8 @@ struct ContainerConfig {
         mGuardPid,
         mInitPid,
         mInit,
-        mLogger
+        mLogger,
+        mTerminals
     )
 };
 
index a524231..715dad8 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -128,9 +129,24 @@ void ContainerImpl::setLogger(const logger::LogType type,
     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();
 }
index 3379143..97b9155 100644 (file)
@@ -54,6 +54,8 @@ public:
                    const logger::LogLevel level,
                    const std::string &arg);
 
+    void setTerminalCount(const unsigned int count);
+
     const std::vector<Namespace>& getNamespaces() const;
 
     // Execution actions
index 3e126d2..bf10a76 100644 (file)
@@ -63,6 +63,8 @@ public:
                            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;
index 11a6a0e..387e2eb 100644 (file)
@@ -71,6 +71,11 @@ struct UtilityException: public Exception {
         : 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) {}
index c8db30b..1065ecb 100644 (file)
@@ -35,15 +35,18 @@ int main(int argc, char *argv[])
         ::_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);
 }
diff --git a/libs/lxcpp/terminal-config.hpp b/libs/lxcpp/terminal-config.hpp
new file mode 100644 (file)
index 0000000..3d6675a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  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
diff --git a/libs/lxcpp/terminal.cpp b/libs/lxcpp/terminal.cpp
new file mode 100644 (file)
index 0000000..ce77cd5
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *  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
diff --git a/libs/lxcpp/terminal.hpp b/libs/lxcpp/terminal.hpp
new file mode 100644 (file)
index 0000000..85ae6e2
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  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
index 41e76b5..685d915 100644 (file)
  * @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");
index 2a59303..e4c158b 100644 (file)
 
 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).