1 //===-- PseudoTerminal.cpp ------------------------------------------------===// 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 #include "lldb/Host/PseudoTerminal.h" 10 #include "lldb/Host/Config.h" 11 12 #include "llvm/Support/Errno.h" 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #if defined(TIOCSCTTY) 18 #include <sys/ioctl.h> 19 #endif 20 21 #include "lldb/Host/PosixApi.h" 22 23 #if defined(__ANDROID__) 24 int posix_openpt(int flags); 25 #endif 26 27 using namespace lldb_private; 28 29 // Write string describing error number 30 static void ErrnoToStr(char *error_str, size_t error_len) { 31 std::string strerror = llvm::sys::StrError(); 32 ::snprintf(error_str, error_len, "%s", strerror.c_str()); 33 } 34 35 // PseudoTerminal constructor 36 PseudoTerminal::PseudoTerminal() 37 : m_primary_fd(invalid_fd), m_secondary_fd(invalid_fd) {} 38 39 // Destructor 40 // 41 // The destructor will close the primary and secondary file descriptors if they 42 // are valid and ownership has not been released using the 43 // ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member 44 // functions. 45 PseudoTerminal::~PseudoTerminal() { 46 ClosePrimaryFileDescriptor(); 47 CloseSecondaryFileDescriptor(); 48 } 49 50 // Close the primary file descriptor if it is valid. 51 void PseudoTerminal::ClosePrimaryFileDescriptor() { 52 if (m_primary_fd >= 0) { 53 ::close(m_primary_fd); 54 m_primary_fd = invalid_fd; 55 } 56 } 57 58 // Close the secondary file descriptor if it is valid. 59 void PseudoTerminal::CloseSecondaryFileDescriptor() { 60 if (m_secondary_fd >= 0) { 61 ::close(m_secondary_fd); 62 m_secondary_fd = invalid_fd; 63 } 64 } 65 66 // Open the first available pseudo terminal with OFLAG as the permissions. The 67 // file descriptor is stored in this object and can be accessed with the 68 // PrimaryFileDescriptor() accessor. The ownership of the primary file 69 // descriptor can be released using the ReleasePrimaryFileDescriptor() accessor. 70 // If this object has a valid primary files descriptor when its destructor is 71 // called, it will close the primary file descriptor, therefore clients must 72 // call ReleasePrimaryFileDescriptor() if they wish to use the primary file 73 // descriptor after this object is out of scope or destroyed. 74 // 75 // RETURNS: 76 // True when successful, false indicating an error occurred. 77 bool PseudoTerminal::OpenFirstAvailablePrimary(int oflag, char *error_str, 78 size_t error_len) { 79 if (error_str) 80 error_str[0] = '\0'; 81 82 #if LLDB_ENABLE_POSIX 83 // Open the primary side of a pseudo terminal 84 m_primary_fd = ::posix_openpt(oflag); 85 if (m_primary_fd < 0) { 86 if (error_str) 87 ErrnoToStr(error_str, error_len); 88 return false; 89 } 90 91 // Grant access to the secondary pseudo terminal 92 if (::grantpt(m_primary_fd) < 0) { 93 if (error_str) 94 ErrnoToStr(error_str, error_len); 95 ClosePrimaryFileDescriptor(); 96 return false; 97 } 98 99 // Clear the lock flag on the secondary pseudo terminal 100 if (::unlockpt(m_primary_fd) < 0) { 101 if (error_str) 102 ErrnoToStr(error_str, error_len); 103 ClosePrimaryFileDescriptor(); 104 return false; 105 } 106 107 return true; 108 #else 109 if (error_str) 110 ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported"); 111 return false; 112 #endif 113 } 114 115 // Open the secondary pseudo terminal for the current primary pseudo terminal. A 116 // primary pseudo terminal should already be valid prior to calling this 117 // function (see OpenFirstAvailablePrimary()). The file descriptor is stored 118 // this object's member variables and can be accessed via the 119 // GetSecondaryFileDescriptor(), or released using the 120 // ReleaseSecondaryFileDescriptor() member function. 121 // 122 // RETURNS: 123 // True when successful, false indicating an error occurred. 124 bool PseudoTerminal::OpenSecondary(int oflag, char *error_str, 125 size_t error_len) { 126 if (error_str) 127 error_str[0] = '\0'; 128 129 CloseSecondaryFileDescriptor(); 130 131 // Open the primary side of a pseudo terminal 132 const char *secondary_name = GetSecondaryName(error_str, error_len); 133 134 if (secondary_name == nullptr) 135 return false; 136 137 m_secondary_fd = 138 llvm::sys::RetryAfterSignal(-1, ::open, secondary_name, oflag); 139 140 if (m_secondary_fd < 0) { 141 if (error_str) 142 ErrnoToStr(error_str, error_len); 143 return false; 144 } 145 146 return true; 147 } 148 149 // Get the name of the secondary pseudo terminal. A primary pseudo terminal 150 // should already be valid prior to calling this function (see 151 // OpenFirstAvailablePrimary()). 152 // 153 // RETURNS: 154 // NULL if no valid primary pseudo terminal or if ptsname() fails. 155 // The name of the secondary pseudo terminal as a NULL terminated C string 156 // that comes from static memory, so a copy of the string should be 157 // made as subsequent calls can change this value. 158 const char *PseudoTerminal::GetSecondaryName(char *error_str, 159 size_t error_len) const { 160 if (error_str) 161 error_str[0] = '\0'; 162 163 if (m_primary_fd < 0) { 164 if (error_str) 165 ::snprintf(error_str, error_len, "%s", 166 "primary file descriptor is invalid"); 167 return nullptr; 168 } 169 const char *secondary_name = ::ptsname(m_primary_fd); 170 171 if (error_str && secondary_name == nullptr) 172 ErrnoToStr(error_str, error_len); 173 174 return secondary_name; 175 } 176 177 // Fork a child process and have its stdio routed to a pseudo terminal. 178 // 179 // In the parent process when a valid pid is returned, the primary file 180 // descriptor can be used as a read/write access to stdio of the child process. 181 // 182 // In the child process the stdin/stdout/stderr will already be routed to the 183 // secondary pseudo terminal and the primary file descriptor will be closed as 184 // it is no longer needed by the child process. 185 // 186 // This class will close the file descriptors for the primary/secondary when the 187 // destructor is called, so be sure to call ReleasePrimaryFileDescriptor() or 188 // ReleaseSecondaryFileDescriptor() if any file descriptors are going to be used 189 // past the lifespan of this object. 190 // 191 // RETURNS: 192 // in the parent process: the pid of the child, or -1 if fork fails 193 // in the child process: zero 194 lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) { 195 if (error_str) 196 error_str[0] = '\0'; 197 pid_t pid = LLDB_INVALID_PROCESS_ID; 198 #if LLDB_ENABLE_POSIX 199 int flags = O_RDWR; 200 flags |= O_CLOEXEC; 201 if (OpenFirstAvailablePrimary(flags, error_str, error_len)) { 202 // Successfully opened our primary pseudo terminal 203 204 pid = ::fork(); 205 if (pid < 0) { 206 // Fork failed 207 if (error_str) 208 ErrnoToStr(error_str, error_len); 209 } else if (pid == 0) { 210 // Child Process 211 ::setsid(); 212 213 if (OpenSecondary(O_RDWR, error_str, error_len)) { 214 // Successfully opened secondary 215 216 // Primary FD should have O_CLOEXEC set, but let's close it just in 217 // case... 218 ClosePrimaryFileDescriptor(); 219 220 #if defined(TIOCSCTTY) 221 // Acquire the controlling terminal 222 if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) { 223 if (error_str) 224 ErrnoToStr(error_str, error_len); 225 } 226 #endif 227 // Duplicate all stdio file descriptors to the secondary pseudo terminal 228 if (::dup2(m_secondary_fd, STDIN_FILENO) != STDIN_FILENO) { 229 if (error_str && !error_str[0]) 230 ErrnoToStr(error_str, error_len); 231 } 232 233 if (::dup2(m_secondary_fd, STDOUT_FILENO) != STDOUT_FILENO) { 234 if (error_str && !error_str[0]) 235 ErrnoToStr(error_str, error_len); 236 } 237 238 if (::dup2(m_secondary_fd, STDERR_FILENO) != STDERR_FILENO) { 239 if (error_str && !error_str[0]) 240 ErrnoToStr(error_str, error_len); 241 } 242 } 243 } else { 244 // Parent Process 245 // Do nothing and let the pid get returned! 246 } 247 } 248 #endif 249 return pid; 250 } 251 252 // The primary file descriptor accessor. This object retains ownership of the 253 // primary file descriptor when this accessor is used. Use 254 // ReleasePrimaryFileDescriptor() if you wish this object to release ownership 255 // of the primary file descriptor. 256 // 257 // Returns the primary file descriptor, or -1 if the primary file descriptor is 258 // not currently valid. 259 int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; } 260 261 // The secondary file descriptor accessor. 262 // 263 // Returns the secondary file descriptor, or -1 if the secondary file descriptor 264 // is not currently valid. 265 int PseudoTerminal::GetSecondaryFileDescriptor() const { 266 return m_secondary_fd; 267 } 268 269 // Release ownership of the primary pseudo terminal file descriptor without 270 // closing it. The destructor for this class will close the primary file 271 // descriptor if the ownership isn't released using this call and the primary 272 // file descriptor has been opened. 273 int PseudoTerminal::ReleasePrimaryFileDescriptor() { 274 // Release ownership of the primary pseudo terminal file descriptor without 275 // closing it. (the destructor for this class will close it otherwise!) 276 int fd = m_primary_fd; 277 m_primary_fd = invalid_fd; 278 return fd; 279 } 280 281 // Release ownership of the secondary pseudo terminal file descriptor without 282 // closing it. The destructor for this class will close the secondary file 283 // descriptor if the ownership isn't released using this call and the secondary 284 // file descriptor has been opened. 285 int PseudoTerminal::ReleaseSecondaryFileDescriptor() { 286 // Release ownership of the secondary pseudo terminal file descriptor without 287 // closing it (the destructor for this class will close it otherwise!) 288 int fd = m_secondary_fd; 289 m_secondary_fd = invalid_fd; 290 return fd; 291 } 292