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