diff options
author | emaste <emaste@FreeBSD.org> | 2013-08-23 18:06:42 +0000 |
---|---|---|
committer | emaste <emaste@FreeBSD.org> | 2013-08-23 18:06:42 +0000 |
commit | 424d4dadd208e2a1e9a43c3d55f47f03ba0c4509 (patch) | |
tree | 05d762b98a499804ce690e6ce04033f1ddf4dee6 /contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp | |
parent | cde487f27a84e02a560384f75178fddca68740f6 (diff) | |
parent | dcd15f81789e389c1cb27d264fcdddfd0a6002bd (diff) | |
download | FreeBSD-src-424d4dadd208e2a1e9a43c3d55f47f03ba0c4509.zip FreeBSD-src-424d4dadd208e2a1e9a43c3d55f47f03ba0c4509.tar.gz |
Merge lldb r188801 to contrib/llvm/tools/lldb/
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp')
-rw-r--r-- | contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp b/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp new file mode 100644 index 0000000..e4b444c --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Utility/PseudoTerminal.cpp @@ -0,0 +1,339 @@ +//===-- 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/Utility/PseudoTerminal.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#if defined(TIOCSCTTY) +#include <sys/ioctl.h> +#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 ownwership 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: +// Zero when successful, non-zero indicating an error occurred. +//---------------------------------------------------------------------- +bool +PseudoTerminal::OpenFirstAvailableMaster (int oflag, char *error_str, size_t error_len) +{ + if (error_str) + error_str[0] = '\0'; + + // 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; +} + +//---------------------------------------------------------------------- +// 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: +// Zero when successful, non-zero 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 == NULL) + 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 NULL; + } + const char *slave_name = ::ptsname (m_master_fd); + + if (error_str && slave_name == NULL) + ::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 (OpenFirstAvailableMaster (O_RDWR, 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 + // We are done with the master in the child process so lets close it + 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! + } + } + 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; +} + |