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 #if !HAVE_PTSNAME_R || defined(__APPLE__)
107 static std::string use_ptsname(int fd) {
108   static std::mutex mutex;
109   std::lock_guard<std::mutex> guard(mutex);
110   const char *r = ptsname(fd);
111   assert(r != nullptr);
112   return r;
113 }
114 #endif
115 
116 std::string PseudoTerminal::GetSecondaryName() const {
117   assert(m_primary_fd >= 0);
118 #if HAVE_PTSNAME_R
119 #if defined(__APPLE__)
120   if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {
121 #endif
122     char buf[PATH_MAX];
123     buf[0] = '\0';
124     int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
125     (void)r;
126     assert(r == 0);
127     return buf;
128 #if defined(__APPLE__)
129   } else {
130     return use_ptsname(m_primary_fd);
131   }
132 #endif
133 #else
134   return use_ptsname(m_primary_fd);
135 #endif
136 }
137 
138 llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
139 #if LLDB_ENABLE_POSIX
140   if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
141     return std::move(Err);
142 
143   pid_t pid = ::fork();
144   if (pid < 0) {
145     return llvm::errorCodeToError(
146         std::error_code(errno, std::generic_category()));
147   }
148   if (pid > 0) {
149     // Parent process.
150     return pid;
151   }
152 
153   // Child Process
154   ::setsid();
155 
156   if (llvm::Error Err = OpenSecondary(O_RDWR))
157     return std::move(Err);
158 
159   // Primary FD should have O_CLOEXEC set, but let's close it just in
160   // case...
161   ClosePrimaryFileDescriptor();
162 
163 #if defined(TIOCSCTTY)
164   // Acquire the controlling terminal
165   if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
166     return llvm::errorCodeToError(
167         std::error_code(errno, std::generic_category()));
168   }
169 #endif
170   // Duplicate all stdio file descriptors to the secondary pseudo terminal
171   for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
172     if (::dup2(m_secondary_fd, fd) != fd) {
173       return llvm::errorCodeToError(
174           std::error_code(errno, std::generic_category()));
175     }
176   }
177 #endif
178   return 0;
179 }
180 
181 // The primary file descriptor accessor. This object retains ownership of the
182 // primary file descriptor when this accessor is used. Use
183 // ReleasePrimaryFileDescriptor() if you wish this object to release ownership
184 // of the primary file descriptor.
185 //
186 // Returns the primary file descriptor, or -1 if the primary file descriptor is
187 // not currently valid.
188 int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
189 
190 // The secondary file descriptor accessor.
191 //
192 // Returns the secondary file descriptor, or -1 if the secondary file descriptor
193 // is not currently valid.
194 int PseudoTerminal::GetSecondaryFileDescriptor() const {
195   return m_secondary_fd;
196 }
197 
198 // Release ownership of the primary pseudo terminal file descriptor without
199 // closing it. The destructor for this class will close the primary file
200 // descriptor if the ownership isn't released using this call and the primary
201 // file descriptor has been opened.
202 int PseudoTerminal::ReleasePrimaryFileDescriptor() {
203   // Release ownership of the primary pseudo terminal file descriptor without
204   // closing it. (the destructor for this class will close it otherwise!)
205   int fd = m_primary_fd;
206   m_primary_fd = invalid_fd;
207   return fd;
208 }
209 
210 // Release ownership of the secondary pseudo terminal file descriptor without
211 // closing it. The destructor for this class will close the secondary file
212 // descriptor if the ownership isn't released using this call and the secondary
213 // file descriptor has been opened.
214 int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
215   // Release ownership of the secondary pseudo terminal file descriptor without
216   // closing it (the destructor for this class will close it otherwise!)
217   int fd = m_secondary_fd;
218   m_secondary_fd = invalid_fd;
219   return fd;
220 }
221