//===-- 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 #include #include #include #if defined(TIOCSCTTY) #include #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; }