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