xref: /openbsd-src/gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
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