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 #include "llvm/Support/Errc.h"
12 #include "llvm/Support/Errno.h"
13 #include <cassert>
14 #include <limits.h>
15 #include <mutex>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #if defined(TIOCSCTTY)
20 #include <sys/ioctl.h>
21 #endif
22 
23 #include "lldb/Host/PosixApi.h"
24 
25 #if defined(__ANDROID__)
26 int posix_openpt(int flags);
27 #endif
28 
29 using namespace lldb_private;
30 
31 // Write string describing error number
32 static void ErrnoToStr(char *error_str, size_t error_len) {
33   std::string strerror = llvm::sys::StrError();
34   ::snprintf(error_str, error_len, "%s", strerror.c_str());
35 }
36 
37 // PseudoTerminal constructor
38 PseudoTerminal::PseudoTerminal()
39     : m_primary_fd(invalid_fd), m_secondary_fd(invalid_fd) {}
40 
41 // Destructor
42 //
43 // The destructor will close the primary and secondary file descriptors if they
44 // are valid and ownership has not been released using the
45 // ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member
46 // functions.
47 PseudoTerminal::~PseudoTerminal() {
48   ClosePrimaryFileDescriptor();
49   CloseSecondaryFileDescriptor();
50 }
51 
52 // Close the primary file descriptor if it is valid.
53 void PseudoTerminal::ClosePrimaryFileDescriptor() {
54   if (m_primary_fd >= 0) {
55     ::close(m_primary_fd);
56     m_primary_fd = invalid_fd;
57   }
58 }
59 
60 // Close the secondary file descriptor if it is valid.
61 void PseudoTerminal::CloseSecondaryFileDescriptor() {
62   if (m_secondary_fd >= 0) {
63     ::close(m_secondary_fd);
64     m_secondary_fd = invalid_fd;
65   }
66 }
67 
68 llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
69 #if LLDB_ENABLE_POSIX
70   // Open the primary side of a pseudo terminal
71   m_primary_fd = ::posix_openpt(oflag);
72   if (m_primary_fd < 0) {
73     return llvm::errorCodeToError(
74         std::error_code(errno, std::generic_category()));
75   }
76 
77   // Grant access to the secondary pseudo terminal
78   if (::grantpt(m_primary_fd) < 0) {
79     std::error_code EC(errno, std::generic_category());
80     ClosePrimaryFileDescriptor();
81     return llvm::errorCodeToError(EC);
82   }
83 
84   // Clear the lock flag on the secondary pseudo terminal
85   if (::unlockpt(m_primary_fd) < 0) {
86     std::error_code EC(errno, std::generic_category());
87     ClosePrimaryFileDescriptor();
88     return llvm::errorCodeToError(EC);
89   }
90 
91   return llvm::Error::success();
92 #else
93   return llvm::errorCodeToError(llvm::errc::not_supported);
94 #endif
95 }
96 
97 llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
98   CloseSecondaryFileDescriptor();
99 
100   std::string name = GetSecondaryName();
101   m_secondary_fd = llvm::sys::RetryAfterSignal(-1, ::open, name.c_str(), oflag);
102   if (m_secondary_fd >= 0)
103     return llvm::Error::success();
104 
105   return llvm::errorCodeToError(
106       std::error_code(errno, std::generic_category()));
107 }
108 
109 std::string PseudoTerminal::GetSecondaryName() const {
110   assert(m_primary_fd >= 0);
111 #if HAVE_PTSNAME_R
112   char buf[PATH_MAX];
113   buf[0] = '\0';
114   int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
115   assert(r == 0);
116   return buf;
117 #else
118   static std::mutex mutex;
119   std::lock_guard<std::mutex> guard(mutex);
120   const char *r = ptsname(m_primary_fd);
121   assert(r != nullptr);
122   return r;
123 #endif
124 }
125 
126 // Fork a child process and have its stdio routed to a pseudo terminal.
127 //
128 // In the parent process when a valid pid is returned, the primary file
129 // descriptor can be used as a read/write access to stdio of the child process.
130 //
131 // In the child process the stdin/stdout/stderr will already be routed to the
132 // secondary pseudo terminal and the primary file descriptor will be closed as
133 // it is no longer needed by the child process.
134 //
135 // This class will close the file descriptors for the primary/secondary when the
136 // destructor is called, so be sure to call ReleasePrimaryFileDescriptor() or
137 // ReleaseSecondaryFileDescriptor() if any file descriptors are going to be used
138 // past the lifespan of this object.
139 //
140 // RETURNS:
141 //  in the parent process: the pid of the child, or -1 if fork fails
142 //  in the child process: zero
143 lldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) {
144   if (error_str)
145     error_str[0] = '\0';
146   pid_t pid = LLDB_INVALID_PROCESS_ID;
147 #if LLDB_ENABLE_POSIX
148   if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC)) {
149     snprintf(error_str, error_len, "%s", toString(std::move(Err)).c_str());
150     return LLDB_INVALID_PROCESS_ID;
151   }
152 
153   pid = ::fork();
154   if (pid < 0) {
155     // Fork failed
156     if (error_str)
157       ErrnoToStr(error_str, error_len);
158   } else if (pid == 0) {
159     // Child Process
160     ::setsid();
161 
162     if (llvm::Error Err = OpenSecondary(O_RDWR)) {
163       snprintf(error_str, error_len, "%s", toString(std::move(Err)).c_str());
164       return LLDB_INVALID_PROCESS_ID;
165     }
166 
167     // Primary FD should have O_CLOEXEC set, but let's close it just in
168     // case...
169     ClosePrimaryFileDescriptor();
170 
171 #if defined(TIOCSCTTY)
172     // Acquire the controlling terminal
173     if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
174       if (error_str)
175         ErrnoToStr(error_str, error_len);
176     }
177 #endif
178     // Duplicate all stdio file descriptors to the secondary pseudo terminal
179     if (::dup2(m_secondary_fd, STDIN_FILENO) != STDIN_FILENO) {
180       if (error_str && !error_str[0])
181         ErrnoToStr(error_str, error_len);
182     }
183 
184     if (::dup2(m_secondary_fd, STDOUT_FILENO) != STDOUT_FILENO) {
185       if (error_str && !error_str[0])
186         ErrnoToStr(error_str, error_len);
187     }
188 
189     if (::dup2(m_secondary_fd, STDERR_FILENO) != STDERR_FILENO) {
190       if (error_str && !error_str[0])
191         ErrnoToStr(error_str, error_len);
192     }
193   } else {
194     // Parent Process
195     // Do nothing and let the pid get returned!
196   }
197 #endif
198   return pid;
199 }
200 
201 // The primary file descriptor accessor. This object retains ownership of the
202 // primary file descriptor when this accessor is used. Use
203 // ReleasePrimaryFileDescriptor() if you wish this object to release ownership
204 // of the primary file descriptor.
205 //
206 // Returns the primary file descriptor, or -1 if the primary file descriptor is
207 // not currently valid.
208 int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
209 
210 // The secondary file descriptor accessor.
211 //
212 // Returns the secondary file descriptor, or -1 if the secondary file descriptor
213 // is not currently valid.
214 int PseudoTerminal::GetSecondaryFileDescriptor() const {
215   return m_secondary_fd;
216 }
217 
218 // Release ownership of the primary pseudo terminal file descriptor without
219 // closing it. The destructor for this class will close the primary file
220 // descriptor if the ownership isn't released using this call and the primary
221 // file descriptor has been opened.
222 int PseudoTerminal::ReleasePrimaryFileDescriptor() {
223   // Release ownership of the primary pseudo terminal file descriptor without
224   // closing it. (the destructor for this class will close it otherwise!)
225   int fd = m_primary_fd;
226   m_primary_fd = invalid_fd;
227   return fd;
228 }
229 
230 // Release ownership of the secondary pseudo terminal file descriptor without
231 // closing it. The destructor for this class will close the secondary file
232 // descriptor if the ownership isn't released using this call and the secondary
233 // file descriptor has been opened.
234 int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
235   // Release ownership of the secondary pseudo terminal file descriptor without
236   // closing it (the destructor for this class will close it otherwise!)
237   int fd = m_secondary_fd;
238   m_secondary_fd = invalid_fd;
239   return fd;
240 }
241