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