PseudoTerminal.cpp revision 24943d2ee8bfaa7cf5893e4709143924157a5c1e
1//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Created by Greg Clayton on 1/8/08. 11// 12//===----------------------------------------------------------------------===// 13 14#include "PseudoTerminal.h" 15#include <stdlib.h> 16#include <sys/ioctl.h> 17 18//---------------------------------------------------------------------- 19// PseudoTerminal constructor 20//---------------------------------------------------------------------- 21PseudoTerminal::PseudoTerminal() : 22 m_master_fd(invalid_fd), 23 m_slave_fd(invalid_fd) 24{ 25} 26 27//---------------------------------------------------------------------- 28// Destructor 29// The master and slave file descriptors will get closed if they are 30// valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions 31// to release any file descriptors that are needed beyond the lifespan 32// of this object. 33//---------------------------------------------------------------------- 34PseudoTerminal::~PseudoTerminal() 35{ 36 CloseMaster(); 37 CloseSlave(); 38} 39 40//---------------------------------------------------------------------- 41// Close the master file descriptor if it is valid. 42//---------------------------------------------------------------------- 43void 44PseudoTerminal::CloseMaster() 45{ 46 if (m_master_fd > 0) 47 { 48 ::close (m_master_fd); 49 m_master_fd = invalid_fd; 50 } 51} 52 53//---------------------------------------------------------------------- 54// Close the slave file descriptor if it is valid. 55//---------------------------------------------------------------------- 56void 57PseudoTerminal::CloseSlave() 58{ 59 if (m_slave_fd > 0) 60 { 61 ::close (m_slave_fd); 62 m_slave_fd = invalid_fd; 63 } 64} 65 66//---------------------------------------------------------------------- 67// Open the first available pseudo terminal with OFLAG as the 68// permissions. The file descriptor is store in the m_master_fd member 69// variable and can be accessed via the MasterFD() or ReleaseMasterFD() 70// accessors. 71// 72// Suggested value for oflag is O_RDWR|O_NOCTTY 73// 74// RETURNS: 75// Zero when successful, non-zero indicating an error occurred. 76//---------------------------------------------------------------------- 77PseudoTerminal::Error 78PseudoTerminal::OpenFirstAvailableMaster(int oflag) 79{ 80 // Open the master side of a pseudo terminal 81 m_master_fd = ::posix_openpt (oflag); 82 if (m_master_fd < 0) 83 { 84 return err_posix_openpt_failed; 85 } 86 87 // Grant access to the slave pseudo terminal 88 if (::grantpt (m_master_fd) < 0) 89 { 90 CloseMaster(); 91 return err_grantpt_failed; 92 } 93 94 // Clear the lock flag on the slave pseudo terminal 95 if (::unlockpt (m_master_fd) < 0) 96 { 97 CloseMaster(); 98 return err_unlockpt_failed; 99 } 100 101 return success; 102} 103 104//---------------------------------------------------------------------- 105// Open the slave pseudo terminal for the current master pseudo 106// terminal. A master pseudo terminal should already be valid prior to 107// calling this function (see PseudoTerminal::OpenFirstAvailableMaster()). 108// The file descriptor is stored in the m_slave_fd member variable and 109// can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors. 110// 111// RETURNS: 112// Zero when successful, non-zero indicating an error occurred. 113//---------------------------------------------------------------------- 114PseudoTerminal::Error 115PseudoTerminal::OpenSlave(int oflag) 116{ 117 CloseSlave(); 118 119 // Open the master side of a pseudo terminal 120 const char *slave_name = SlaveName(); 121 122 if (slave_name == NULL) 123 return err_ptsname_failed; 124 125 m_slave_fd = ::open (slave_name, oflag); 126 127 if (m_slave_fd < 0) 128 return err_open_slave_failed; 129 130 return success; 131} 132 133 134 135//---------------------------------------------------------------------- 136// Get the name of the slave pseudo terminal. A master pseudo terminal 137// should already be valid prior to calling this function (see 138// PseudoTerminal::OpenFirstAvailableMaster()). 139// 140// RETURNS: 141// NULL if no valid master pseudo terminal or if ptsname() fails. 142// The name of the slave pseudo terminal as a NULL terminated C string 143// that comes from static memory, so a copy of the string should be 144// made as subsequent calls can change this value. 145//---------------------------------------------------------------------- 146const char* 147PseudoTerminal::SlaveName() const 148{ 149 if (m_master_fd < 0) 150 return NULL; 151 return ::ptsname (m_master_fd); 152} 153 154 155//---------------------------------------------------------------------- 156// Fork a child process that and have its stdio routed to a pseudo 157// terminal. 158// 159// In the parent process when a valid pid is returned, the master file 160// descriptor can be used as a read/write access to stdio of the 161// child process. 162// 163// In the child process the stdin/stdout/stderr will already be routed 164// to the slave pseudo terminal and the master file descriptor will be 165// closed as it is no longer needed by the child process. 166// 167// This class will close the file descriptors for the master/slave 168// when the destructor is called, so be sure to call ReleaseMasterFD() 169// or ReleaseSlaveFD() if any file descriptors are going to be used 170// past the lifespan of this object. 171// 172// RETURNS: 173// in the parent process: the pid of the child, or -1 if fork fails 174// in the child process: zero 175//---------------------------------------------------------------------- 176 177pid_t 178PseudoTerminal::Fork(PseudoTerminal::Error& error) 179{ 180 pid_t pid = invalid_pid; 181 error = OpenFirstAvailableMaster (O_RDWR|O_NOCTTY); 182 183 if (error == 0) 184 { 185 // Successfully opened our master pseudo terminal 186 187 pid = ::fork (); 188 if (pid < 0) 189 { 190 // Fork failed 191 error = err_fork_failed; 192 } 193 else if (pid == 0) 194 { 195 // Child Process 196 ::setsid(); 197 198 error = OpenSlave (O_RDWR); 199 if (error == 0) 200 { 201 // Successfully opened slave 202 // We are done with the master in the child process so lets close it 203 CloseMaster (); 204 205#if defined (TIOCSCTTY) 206 // Acquire the controlling terminal 207 if (::ioctl (m_slave_fd, TIOCSCTTY, (char *)0) < 0) 208 error = err_failed_to_acquire_controlling_terminal; 209#endif 210 // Duplicate all stdio file descriptors to the slave pseudo terminal 211 if (::dup2 (m_slave_fd, STDIN_FILENO) != STDIN_FILENO) 212 error = error ? error : err_dup2_failed_on_stdin; 213 if (::dup2 (m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) 214 error = error ? error : err_dup2_failed_on_stdout; 215 if (::dup2 (m_slave_fd, STDERR_FILENO) != STDERR_FILENO) 216 error = error ? error : err_dup2_failed_on_stderr; 217 } 218 } 219 else 220 { 221 // Parent Process 222 // Do nothing and let the pid get returned! 223 } 224 } 225 return pid; 226} 227