forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			311 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			311 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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;
 | |
| }
 |