PseudoTerminal.cpp revision 341825
1317027Sdim//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// 2317027Sdim// 3317027Sdim// The LLVM Compiler Infrastructure 4317027Sdim// 5317027Sdim// This file is distributed under the University of Illinois Open Source 6317027Sdim// License. See LICENSE.TXT for details. 7317027Sdim// 8317027Sdim//===----------------------------------------------------------------------===// 9317027Sdim 10317027Sdim#include "lldb/Host/PseudoTerminal.h" 11317027Sdim#include "lldb/Host/Config.h" 12317027Sdim 13317027Sdim#include <errno.h> 14317027Sdim#include <stdio.h> 15317027Sdim#include <stdlib.h> 16317027Sdim#include <string.h> 17317027Sdim#if defined(TIOCSCTTY) 18317027Sdim#include <sys/ioctl.h> 19317027Sdim#endif 20317027Sdim 21317027Sdim#include "lldb/Host/PosixApi.h" 22317027Sdim 23317027Sdim#if defined(__ANDROID__) 24317027Sdimint posix_openpt(int flags); 25317027Sdim#endif 26317027Sdim 27327952Sdimusing namespace lldb_private; 28317027Sdim 29317027Sdim//---------------------------------------------------------------------- 30317027Sdim// PseudoTerminal constructor 31317027Sdim//---------------------------------------------------------------------- 32317027SdimPseudoTerminal::PseudoTerminal() 33317027Sdim : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {} 34317027Sdim 35317027Sdim//---------------------------------------------------------------------- 36317027Sdim// Destructor 37317027Sdim// 38341825Sdim// The destructor will close the master and slave file descriptors if they are 39341825Sdim// valid and ownership has not been released using the 40341825Sdim// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() member 41341825Sdim// functions. 42317027Sdim//---------------------------------------------------------------------- 43317027SdimPseudoTerminal::~PseudoTerminal() { 44317027Sdim CloseMasterFileDescriptor(); 45317027Sdim CloseSlaveFileDescriptor(); 46317027Sdim} 47317027Sdim 48317027Sdim//---------------------------------------------------------------------- 49317027Sdim// Close the master file descriptor if it is valid. 50317027Sdim//---------------------------------------------------------------------- 51317027Sdimvoid PseudoTerminal::CloseMasterFileDescriptor() { 52317027Sdim if (m_master_fd >= 0) { 53317027Sdim ::close(m_master_fd); 54317027Sdim m_master_fd = invalid_fd; 55317027Sdim } 56317027Sdim} 57317027Sdim 58317027Sdim//---------------------------------------------------------------------- 59317027Sdim// Close the slave file descriptor if it is valid. 60317027Sdim//---------------------------------------------------------------------- 61317027Sdimvoid PseudoTerminal::CloseSlaveFileDescriptor() { 62317027Sdim if (m_slave_fd >= 0) { 63317027Sdim ::close(m_slave_fd); 64317027Sdim m_slave_fd = invalid_fd; 65317027Sdim } 66317027Sdim} 67317027Sdim 68317027Sdim//---------------------------------------------------------------------- 69341825Sdim// Open the first available pseudo terminal with OFLAG as the permissions. The 70341825Sdim// file descriptor is stored in this object and can be accessed with the 71341825Sdim// MasterFileDescriptor() accessor. The ownership of the master file descriptor 72341825Sdim// can be released using the ReleaseMasterFileDescriptor() accessor. If this 73341825Sdim// object has a valid master files descriptor when its destructor is called, it 74341825Sdim// will close the master file descriptor, therefore clients must call 75341825Sdim// ReleaseMasterFileDescriptor() if they wish to use the master file descriptor 76341825Sdim// after this object is out of scope or destroyed. 77317027Sdim// 78317027Sdim// RETURNS: 79317027Sdim// True when successful, false indicating an error occurred. 80317027Sdim//---------------------------------------------------------------------- 81317027Sdimbool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str, 82317027Sdim size_t error_len) { 83317027Sdim if (error_str) 84317027Sdim error_str[0] = '\0'; 85317027Sdim 86317027Sdim#if !defined(LLDB_DISABLE_POSIX) 87317027Sdim // Open the master side of a pseudo terminal 88317027Sdim m_master_fd = ::posix_openpt(oflag); 89317027Sdim if (m_master_fd < 0) { 90317027Sdim if (error_str) 91317027Sdim ::strerror_r(errno, error_str, error_len); 92317027Sdim return false; 93317027Sdim } 94317027Sdim 95317027Sdim // Grant access to the slave pseudo terminal 96317027Sdim if (::grantpt(m_master_fd) < 0) { 97317027Sdim if (error_str) 98317027Sdim ::strerror_r(errno, error_str, error_len); 99317027Sdim CloseMasterFileDescriptor(); 100317027Sdim return false; 101317027Sdim } 102317027Sdim 103317027Sdim // Clear the lock flag on the slave pseudo terminal 104317027Sdim if (::unlockpt(m_master_fd) < 0) { 105317027Sdim if (error_str) 106317027Sdim ::strerror_r(errno, error_str, error_len); 107317027Sdim CloseMasterFileDescriptor(); 108317027Sdim return false; 109317027Sdim } 110317027Sdim 111317027Sdim return true; 112317027Sdim#else 113317027Sdim if (error_str) 114317027Sdim ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported"); 115317027Sdim return false; 116317027Sdim#endif 117317027Sdim} 118317027Sdim 119317027Sdim//---------------------------------------------------------------------- 120341825Sdim// Open the slave pseudo terminal for the current master pseudo terminal. A 121341825Sdim// master pseudo terminal should already be valid prior to calling this 122341825Sdim// function (see OpenFirstAvailableMaster()). The file descriptor is stored 123341825Sdim// this object's member variables and can be accessed via the 124341825Sdim// GetSlaveFileDescriptor(), or released using the ReleaseSlaveFileDescriptor() 125341825Sdim// member function. 126317027Sdim// 127317027Sdim// RETURNS: 128317027Sdim// True when successful, false indicating an error occurred. 129317027Sdim//---------------------------------------------------------------------- 130317027Sdimbool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) { 131317027Sdim if (error_str) 132317027Sdim error_str[0] = '\0'; 133317027Sdim 134317027Sdim CloseSlaveFileDescriptor(); 135317027Sdim 136317027Sdim // Open the master side of a pseudo terminal 137317027Sdim const char *slave_name = GetSlaveName(error_str, error_len); 138317027Sdim 139317027Sdim if (slave_name == nullptr) 140317027Sdim return false; 141317027Sdim 142317027Sdim m_slave_fd = ::open(slave_name, oflag); 143317027Sdim 144317027Sdim if (m_slave_fd < 0) { 145317027Sdim if (error_str) 146317027Sdim ::strerror_r(errno, error_str, error_len); 147317027Sdim return false; 148317027Sdim } 149317027Sdim 150317027Sdim return true; 151317027Sdim} 152317027Sdim 153317027Sdim//---------------------------------------------------------------------- 154341825Sdim// Get the name of the slave pseudo terminal. A master pseudo terminal should 155341825Sdim// already be valid prior to calling this function (see 156317027Sdim// OpenFirstAvailableMaster()). 157317027Sdim// 158317027Sdim// RETURNS: 159317027Sdim// NULL if no valid master pseudo terminal or if ptsname() fails. 160317027Sdim// The name of the slave pseudo terminal as a NULL terminated C string 161317027Sdim// that comes from static memory, so a copy of the string should be 162317027Sdim// made as subsequent calls can change this value. 163317027Sdim//---------------------------------------------------------------------- 164317027Sdimconst char *PseudoTerminal::GetSlaveName(char *error_str, 165317027Sdim size_t error_len) const { 166317027Sdim if (error_str) 167317027Sdim error_str[0] = '\0'; 168317027Sdim 169317027Sdim if (m_master_fd < 0) { 170317027Sdim if (error_str) 171317027Sdim ::snprintf(error_str, error_len, "%s", 172317027Sdim "master file descriptor is invalid"); 173317027Sdim return nullptr; 174317027Sdim } 175317027Sdim const char *slave_name = ::ptsname(m_master_fd); 176317027Sdim 177317027Sdim if (error_str && slave_name == nullptr) 178317027Sdim ::strerror_r(errno, error_str, error_len); 179317027Sdim 180317027Sdim return slave_name; 181317027Sdim} 182317027Sdim 183317027Sdim//---------------------------------------------------------------------- 184317027Sdim// Fork a child process and have its stdio routed to a pseudo terminal. 185317027Sdim// 186317027Sdim// In the parent process when a valid pid is returned, the master file 187341825Sdim// descriptor can be used as a read/write access to stdio of the child process. 188317027Sdim// 189341825Sdim// In the child process the stdin/stdout/stderr will already be routed to the 190341825Sdim// slave pseudo terminal and the master file descriptor will be closed as it is 191341825Sdim// no longer needed by the child process. 192317027Sdim// 193341825Sdim// This class will close the file descriptors for the master/slave when the 194341825Sdim// destructor is called, so be sure to call ReleaseMasterFileDescriptor() or 195341825Sdim// ReleaseSlaveFileDescriptor() if any file descriptors are going to be used 196341825Sdim// past the lifespan of this object. 197317027Sdim// 198317027Sdim// RETURNS: 199317027Sdim// in the parent process: the pid of the child, or -1 if fork fails 200317027Sdim// in the child process: zero 201317027Sdim//---------------------------------------------------------------------- 202317027Sdimlldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) { 203317027Sdim if (error_str) 204317027Sdim error_str[0] = '\0'; 205317027Sdim pid_t pid = LLDB_INVALID_PROCESS_ID; 206317027Sdim#if !defined(LLDB_DISABLE_POSIX) 207317027Sdim int flags = O_RDWR; 208317027Sdim flags |= O_CLOEXEC; 209317027Sdim if (OpenFirstAvailableMaster(flags, error_str, error_len)) { 210317027Sdim // Successfully opened our master pseudo terminal 211317027Sdim 212317027Sdim pid = ::fork(); 213317027Sdim if (pid < 0) { 214317027Sdim // Fork failed 215317027Sdim if (error_str) 216317027Sdim ::strerror_r(errno, error_str, error_len); 217317027Sdim } else if (pid == 0) { 218317027Sdim // Child Process 219317027Sdim ::setsid(); 220317027Sdim 221317027Sdim if (OpenSlave(O_RDWR, error_str, error_len)) { 222317027Sdim // Successfully opened slave 223317027Sdim 224317027Sdim // Master FD should have O_CLOEXEC set, but let's close it just in 225317027Sdim // case... 226317027Sdim CloseMasterFileDescriptor(); 227317027Sdim 228317027Sdim#if defined(TIOCSCTTY) 229317027Sdim // Acquire the controlling terminal 230317027Sdim if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) { 231317027Sdim if (error_str) 232317027Sdim ::strerror_r(errno, error_str, error_len); 233317027Sdim } 234317027Sdim#endif 235317027Sdim // Duplicate all stdio file descriptors to the slave pseudo terminal 236317027Sdim if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) { 237317027Sdim if (error_str && !error_str[0]) 238317027Sdim ::strerror_r(errno, error_str, error_len); 239317027Sdim } 240317027Sdim 241317027Sdim if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) { 242317027Sdim if (error_str && !error_str[0]) 243317027Sdim ::strerror_r(errno, error_str, error_len); 244317027Sdim } 245317027Sdim 246317027Sdim if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) { 247317027Sdim if (error_str && !error_str[0]) 248317027Sdim ::strerror_r(errno, error_str, error_len); 249317027Sdim } 250317027Sdim } 251317027Sdim } else { 252317027Sdim // Parent Process 253317027Sdim // Do nothing and let the pid get returned! 254317027Sdim } 255317027Sdim } 256317027Sdim#endif 257317027Sdim return pid; 258317027Sdim} 259317027Sdim 260317027Sdim//---------------------------------------------------------------------- 261341825Sdim// The master file descriptor accessor. This object retains ownership of the 262341825Sdim// master file descriptor when this accessor is used. Use 263341825Sdim// ReleaseMasterFileDescriptor() if you wish this object to release ownership 264341825Sdim// of the master file descriptor. 265317027Sdim// 266341825Sdim// Returns the master file descriptor, or -1 if the master file descriptor is 267341825Sdim// not currently valid. 268317027Sdim//---------------------------------------------------------------------- 269317027Sdimint PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; } 270317027Sdim 271317027Sdim//---------------------------------------------------------------------- 272317027Sdim// The slave file descriptor accessor. 273317027Sdim// 274341825Sdim// Returns the slave file descriptor, or -1 if the slave file descriptor is not 275341825Sdim// currently valid. 276317027Sdim//---------------------------------------------------------------------- 277317027Sdimint PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; } 278317027Sdim 279317027Sdim//---------------------------------------------------------------------- 280341825Sdim// Release ownership of the master pseudo terminal file descriptor without 281341825Sdim// closing it. The destructor for this class will close the master file 282341825Sdim// descriptor if the ownership isn't released using this call and the master 283341825Sdim// file descriptor has been opened. 284317027Sdim//---------------------------------------------------------------------- 285317027Sdimint PseudoTerminal::ReleaseMasterFileDescriptor() { 286341825Sdim // Release ownership of the master pseudo terminal file descriptor without 287341825Sdim // closing it. (the destructor for this class will close it otherwise!) 288317027Sdim int fd = m_master_fd; 289317027Sdim m_master_fd = invalid_fd; 290317027Sdim return fd; 291317027Sdim} 292317027Sdim 293317027Sdim//---------------------------------------------------------------------- 294341825Sdim// Release ownership of the slave pseudo terminal file descriptor without 295341825Sdim// closing it. The destructor for this class will close the slave file 296341825Sdim// descriptor if the ownership isn't released using this call and the slave 297341825Sdim// file descriptor has been opened. 298317027Sdim//---------------------------------------------------------------------- 299317027Sdimint PseudoTerminal::ReleaseSlaveFileDescriptor() { 300341825Sdim // Release ownership of the slave pseudo terminal file descriptor without 301341825Sdim // closing it (the destructor for this class will close it otherwise!) 302317027Sdim int fd = m_slave_fd; 303317027Sdim m_slave_fd = invalid_fd; 304317027Sdim return fd; 305317027Sdim} 306