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