Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
39607 views
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.
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
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
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__)
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
117
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
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.
189
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.
195
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.
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.
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
223