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