aboutsummaryrefslogtreecommitdiff
path: root/source/Host/common/PseudoTerminal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/common/PseudoTerminal.cpp')
-rw-r--r--source/Host/common/PseudoTerminal.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/source/Host/common/PseudoTerminal.cpp b/source/Host/common/PseudoTerminal.cpp
new file mode 100644
index 000000000000..58c32e4a1c4b
--- /dev/null
+++ b/source/Host/common/PseudoTerminal.cpp
@@ -0,0 +1,310 @@
+//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/PseudoTerminal.h"
+#include "lldb/Host/Config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(TIOCSCTTY)
+#include <sys/ioctl.h>
+#endif
+
+#include "lldb/Host/PosixApi.h"
+
+#if defined(__ANDROID__)
+int posix_openpt(int flags);
+#endif
+
+using namespace lldb_utility;
+
+//----------------------------------------------------------------------
+// PseudoTerminal constructor
+//----------------------------------------------------------------------
+PseudoTerminal::PseudoTerminal()
+ : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {}
+
+//----------------------------------------------------------------------
+// Destructor
+//
+// The destructor will close the master and slave file descriptors
+// if they are valid and ownership has not been released using the
+// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor()
+// member functions.
+//----------------------------------------------------------------------
+PseudoTerminal::~PseudoTerminal() {
+ CloseMasterFileDescriptor();
+ CloseSlaveFileDescriptor();
+}
+
+//----------------------------------------------------------------------
+// Close the master file descriptor if it is valid.
+//----------------------------------------------------------------------
+void PseudoTerminal::CloseMasterFileDescriptor() {
+ if (m_master_fd >= 0) {
+ ::close(m_master_fd);
+ m_master_fd = invalid_fd;
+ }
+}
+
+//----------------------------------------------------------------------
+// Close the slave file descriptor if it is valid.
+//----------------------------------------------------------------------
+void PseudoTerminal::CloseSlaveFileDescriptor() {
+ if (m_slave_fd >= 0) {
+ ::close(m_slave_fd);
+ m_slave_fd = invalid_fd;
+ }
+}
+
+//----------------------------------------------------------------------
+// Open the first available pseudo terminal with OFLAG as the
+// permissions. The file descriptor is stored in this object and can
+// be accessed with the MasterFileDescriptor() accessor. The
+// ownership of the master file descriptor can be released using
+// the ReleaseMasterFileDescriptor() accessor. If this object has
+// a valid master files descriptor when its destructor is called, it
+// will close the master file descriptor, therefore clients must
+// call ReleaseMasterFileDescriptor() if they wish to use the master
+// file descriptor after this object is out of scope or destroyed.
+//
+// RETURNS:
+// True when successful, false indicating an error occurred.
+//----------------------------------------------------------------------
+bool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str,
+ size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+
+#if !defined(LLDB_DISABLE_POSIX)
+ // Open the master side of a pseudo terminal
+ m_master_fd = ::posix_openpt(oflag);
+ if (m_master_fd < 0) {
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ return false;
+ }
+
+ // Grant access to the slave pseudo terminal
+ if (::grantpt(m_master_fd) < 0) {
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ CloseMasterFileDescriptor();
+ return false;
+ }
+
+ // Clear the lock flag on the slave pseudo terminal
+ if (::unlockpt(m_master_fd) < 0) {
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ CloseMasterFileDescriptor();
+ return false;
+ }
+
+ return true;
+#else
+ if (error_str)
+ ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported");
+ return false;
+#endif
+}
+
+//----------------------------------------------------------------------
+// Open the slave pseudo terminal for the current master pseudo
+// terminal. A master pseudo terminal should already be valid prior to
+// calling this function (see OpenFirstAvailableMaster()).
+// The file descriptor is stored this object's member variables and can
+// be accessed via the GetSlaveFileDescriptor(), or released using the
+// ReleaseSlaveFileDescriptor() member function.
+//
+// RETURNS:
+// True when successful, false indicating an error occurred.
+//----------------------------------------------------------------------
+bool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+
+ CloseSlaveFileDescriptor();
+
+ // Open the master side of a pseudo terminal
+ const char *slave_name = GetSlaveName(error_str, error_len);
+
+ if (slave_name == nullptr)
+ return false;
+
+ m_slave_fd = ::open(slave_name, oflag);
+
+ if (m_slave_fd < 0) {
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+// Get the name of the slave pseudo terminal. A master pseudo terminal
+// should already be valid prior to calling this function (see
+// OpenFirstAvailableMaster()).
+//
+// RETURNS:
+// NULL if no valid master pseudo terminal or if ptsname() fails.
+// The name of the slave pseudo terminal as a NULL terminated C string
+// that comes from static memory, so a copy of the string should be
+// made as subsequent calls can change this value.
+//----------------------------------------------------------------------
+const char *PseudoTerminal::GetSlaveName(char *error_str,
+ size_t error_len) const {
+ if (error_str)
+ error_str[0] = '\0';
+
+ if (m_master_fd < 0) {
+ if (error_str)
+ ::snprintf(error_str, error_len, "%s",
+ "master file descriptor is invalid");
+ return nullptr;
+ }
+ const char *slave_name = ::ptsname(m_master_fd);
+
+ if (error_str && slave_name == nullptr)
+ ::strerror_r(errno, error_str, error_len);
+
+ return slave_name;
+}
+
+//----------------------------------------------------------------------
+// Fork a child process and have its stdio routed to a pseudo terminal.
+//
+// In the parent process when a valid pid is returned, the master file
+// descriptor can be used as a read/write access to stdio of the
+// child process.
+//
+// In the child process the stdin/stdout/stderr will already be routed
+// to the slave pseudo terminal and the master file descriptor will be
+// closed as it is no longer needed by the child process.
+//
+// This class will close the file descriptors for the master/slave
+// when the destructor is called, so be sure to call
+// ReleaseMasterFileDescriptor() or ReleaseSlaveFileDescriptor() if any
+// file descriptors are going to be used past the lifespan of this
+// object.
+//
+// RETURNS:
+// in the parent process: the pid of the child, or -1 if fork fails
+// in the child process: zero
+//----------------------------------------------------------------------
+lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) {
+ if (error_str)
+ error_str[0] = '\0';
+ pid_t pid = LLDB_INVALID_PROCESS_ID;
+#if !defined(LLDB_DISABLE_POSIX)
+ int flags = O_RDWR;
+ flags |= O_CLOEXEC;
+ if (OpenFirstAvailableMaster(flags, error_str, error_len)) {
+ // Successfully opened our master pseudo terminal
+
+ pid = ::fork();
+ if (pid < 0) {
+ // Fork failed
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ } else if (pid == 0) {
+ // Child Process
+ ::setsid();
+
+ if (OpenSlave(O_RDWR, error_str, error_len)) {
+ // Successfully opened slave
+
+ // Master FD should have O_CLOEXEC set, but let's close it just in
+ // case...
+ CloseMasterFileDescriptor();
+
+#if defined(TIOCSCTTY)
+ // Acquire the controlling terminal
+ if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) {
+ if (error_str)
+ ::strerror_r(errno, error_str, error_len);
+ }
+#endif
+ // Duplicate all stdio file descriptors to the slave pseudo terminal
+ if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) {
+ if (error_str && !error_str[0])
+ ::strerror_r(errno, error_str, error_len);
+ }
+
+ if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) {
+ if (error_str && !error_str[0])
+ ::strerror_r(errno, error_str, error_len);
+ }
+
+ if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) {
+ if (error_str && !error_str[0])
+ ::strerror_r(errno, error_str, error_len);
+ }
+ }
+ } else {
+ // Parent Process
+ // Do nothing and let the pid get returned!
+ }
+ }
+#endif
+ return pid;
+}
+
+//----------------------------------------------------------------------
+// The master file descriptor accessor. This object retains ownership
+// of the master file descriptor when this accessor is used. Use
+// ReleaseMasterFileDescriptor() if you wish this object to release
+// ownership of the master file descriptor.
+//
+// Returns the master file descriptor, or -1 if the master file
+// descriptor is not currently valid.
+//----------------------------------------------------------------------
+int PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; }
+
+//----------------------------------------------------------------------
+// The slave file descriptor accessor.
+//
+// Returns the slave file descriptor, or -1 if the slave file
+// descriptor is not currently valid.
+//----------------------------------------------------------------------
+int PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; }
+
+//----------------------------------------------------------------------
+// Release ownership of the master pseudo terminal file descriptor
+// without closing it. The destructor for this class will close the
+// master file descriptor if the ownership isn't released using this
+// call and the master file descriptor has been opened.
+//----------------------------------------------------------------------
+int PseudoTerminal::ReleaseMasterFileDescriptor() {
+ // Release ownership of the master pseudo terminal file
+ // descriptor without closing it. (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_master_fd;
+ m_master_fd = invalid_fd;
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// Release ownership of the slave pseudo terminal file descriptor
+// without closing it. The destructor for this class will close the
+// slave file descriptor if the ownership isn't released using this
+// call and the slave file descriptor has been opened.
+//----------------------------------------------------------------------
+int PseudoTerminal::ReleaseSlaveFileDescriptor() {
+ // Release ownership of the slave pseudo terminal file
+ // descriptor without closing it (the destructor for this
+ // class will close it otherwise!)
+ int fd = m_slave_fd;
+ m_slave_fd = invalid_fd;
+ return fd;
+}