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 //---------------------------------------------------------------------- 21 PseudoTerminal::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 //---------------------------------------------------------------------- 34 PseudoTerminal::~PseudoTerminal() 35 { 36 CloseMaster(); 37 CloseSlave(); 38 } 39 40 //---------------------------------------------------------------------- 41 // Close the master file descriptor if it is valid. 42 //---------------------------------------------------------------------- 43 void 44 PseudoTerminal::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 //---------------------------------------------------------------------- 56 void 57 PseudoTerminal::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 //---------------------------------------------------------------------- 77 PseudoTerminal::Error 78 PseudoTerminal::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 //---------------------------------------------------------------------- 114 PseudoTerminal::Error 115 PseudoTerminal::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 //---------------------------------------------------------------------- 146 const char* 147 PseudoTerminal::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 177 pid_t 178 PseudoTerminal::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