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 //---------------------------------------------------------------------- 22 PseudoTerminal::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 //---------------------------------------------------------------------- 35 PseudoTerminal::~PseudoTerminal() 36 { 37 CloseMaster(); 38 CloseSlave(); 39 } 40 41 //---------------------------------------------------------------------- 42 // Close the master file descriptor if it is valid. 43 //---------------------------------------------------------------------- 44 void 45 PseudoTerminal::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 //---------------------------------------------------------------------- 57 void 58 PseudoTerminal::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 //---------------------------------------------------------------------- 78 PseudoTerminal::Error 79 PseudoTerminal::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 //---------------------------------------------------------------------- 115 PseudoTerminal::Error 116 PseudoTerminal::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 //---------------------------------------------------------------------- 147 const char* 148 PseudoTerminal::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 178 pid_t 179 PseudoTerminal::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