LLDB mainline
PseudoTerminal.cpp
Go to the documentation of this file.
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
10#include "lldb/Host/Config.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
30using namespace lldb_private;
31
32// PseudoTerminal constructor
34
35// Destructor
36//
37// The destructor will close the primary and secondary file descriptors if they
38// are valid and ownership has not been released using the
39// ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member
40// functions.
42
43// Close the primary file descriptor if it is valid.
50
51// Close the secondary file descriptor if it is valid.
58
63
65 Reset();
66#if LLDB_ENABLE_POSIX
67#if defined(__OpenBSD__)
68 // OpenBSD's posix_openpt() fails with EINVAL if O_CLOEXEC is passed, so set
69 // FD_CLOEXEC after opening instead.
70 const bool cloexec = oflag & O_CLOEXEC;
71 oflag &= ~O_CLOEXEC;
72#endif
73
74 // Open the primary side of a pseudo terminal
76 if (m_primary_fd < 0) {
77 return llvm::errorCodeToError(
78 std::error_code(errno, std::generic_category()));
79 }
80
81#if defined(__OpenBSD__)
82 if (cloexec && ::fcntl(m_primary_fd, F_SETFD, FD_CLOEXEC) == -1) {
83 std::error_code EC(errno, std::generic_category());
85 return llvm::errorCodeToError(EC);
86 }
87#endif
88
89 // Grant access to the secondary pseudo terminal
90 if (::grantpt(m_primary_fd) < 0) {
91 std::error_code EC(errno, std::generic_category());
93 return llvm::errorCodeToError(EC);
94 }
95
96 // Clear the lock flag on the secondary pseudo terminal
97 if (::unlockpt(m_primary_fd) < 0) {
98 std::error_code EC(errno, std::generic_category());
100 return llvm::errorCodeToError(EC);
101 }
102
103 return llvm::Error::success();
104#else
105 return llvm::errorCodeToError(llvm::errc::not_supported);
106#endif
107}
108
109llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
111
112 std::string name = GetSecondaryName();
113 m_secondary_fd = FileSystem::Instance().Open(name.c_str(), oflag);
114 if (m_secondary_fd >= 0)
115 return llvm::Error::success();
116
117 return llvm::errorCodeToError(
118 std::error_code(errno, std::generic_category()));
119}
120
121#if !HAVE_PTSNAME_R || defined(__APPLE__)
122static std::string use_ptsname(int fd) {
123 static std::mutex mutex;
124 std::lock_guard<std::mutex> guard(mutex);
125 const char *r = ptsname(fd);
126 assert(r != nullptr);
127 return r;
128}
129#endif
130
132 assert(m_primary_fd >= 0);
133#if HAVE_PTSNAME_R
134#if defined(__APPLE__)
135 if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {
136#endif
137 char buf[PATH_MAX];
138 buf[0] = '\0';
139 int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
141 assert(r == 0);
142 return buf;
143#if defined(__APPLE__)
144 } else {
146 }
147#endif
148#else
150#endif
151}
152
153llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
154#if LLDB_ENABLE_POSIX
155 if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
156 return std::move(Err);
157
158 pid_t pid = ::fork();
159 if (pid < 0) {
160 return llvm::errorCodeToError(
161 std::error_code(errno, std::generic_category()));
162 }
163 if (pid > 0) {
164 // Parent process.
165 return pid;
166 }
167
168 // Child Process
169 ::setsid();
170
171 if (llvm::Error Err = OpenSecondary(O_RDWR))
172 return std::move(Err);
173
174 // Primary FD should have O_CLOEXEC set, but let's close it just in
175 // case...
177
178#if defined(TIOCSCTTY)
179 // Acquire the controlling terminal
180 if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
181 return llvm::errorCodeToError(
182 std::error_code(errno, std::generic_category()));
183 }
184#endif
185 // Duplicate all stdio file descriptors to the secondary pseudo terminal
186 for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
187 if (::dup2(m_secondary_fd, fd) != fd) {
188 return llvm::errorCodeToError(
189 std::error_code(errno, std::generic_category()));
190 }
191 }
192#endif
193 return 0;
194}
195
196// The primary file descriptor accessor. This object retains ownership of the
197// primary file descriptor when this accessor is used. Use
198// ReleasePrimaryFileDescriptor() if you wish this object to release ownership
199// of the primary file descriptor.
200//
201// Returns the primary file descriptor, or -1 if the primary file descriptor is
202// not currently valid.
204
205// The secondary file descriptor accessor.
206//
207// Returns the secondary file descriptor, or -1 if the secondary file descriptor
208// is not currently valid.
212
213// Release ownership of the primary pseudo terminal file descriptor without
214// closing it. The destructor for this class will close the primary file
215// descriptor if the ownership isn't released using this call and the primary
216// file descriptor has been opened.
218 // Release ownership of the primary pseudo terminal file descriptor without
219 // closing it. (the destructor for this class will close it otherwise!)
220 int fd = m_primary_fd;
222 return fd;
223}
224
225// Release ownership of the secondary pseudo terminal file descriptor without
226// closing it. The destructor for this class will close the secondary file
227// descriptor if the ownership isn't released using this call and the secondary
228// file descriptor has been opened.
230 // Release ownership of the secondary pseudo terminal file descriptor without
231 // closing it (the destructor for this class will close it otherwise!)
232 int fd = m_secondary_fd;
234 return fd;
235}
static std::string use_ptsname(int fd)
int Open(const char *path, int flags, int mode=0600)
Wraps open in a platform-independent way.
static FileSystem & Instance()
PseudoTerminal()
Constructs this object with invalid primary and secondary file descriptors.
int m_secondary_fd
The file descriptor for the secondary.
int ReleaseSecondaryFileDescriptor()
Release the secondary file descriptor.
llvm::Error OpenFirstAvailablePrimary(int oflag)
Open the first available pseudo terminal.
llvm::Error OpenSecondary(int oflag)
Open the secondary for the current primary pseudo terminal.
void ClosePrimaryFileDescriptor()
Close the primary file descriptor if it is valid.
int m_primary_fd
The file descriptor for the primary.
llvm::Expected< lldb::pid_t > Fork()
Fork a child process that uses pseudo terminals for its stdio.
void Reset()
Close both file descriptors and clear cached state.
@ invalid_fd
Invalid file descriptor value.
int GetPrimaryFileDescriptor() const
The primary file descriptor accessor.
int ReleasePrimaryFileDescriptor()
Release the primary file descriptor.
std::string GetSecondaryName() const
Get the name of the secondary pseudo terminal.
void CloseSecondaryFileDescriptor()
Close the secondary file descriptor if it is valid.
int GetSecondaryFileDescriptor() const
The secondary file descriptor accessor.
~PseudoTerminal()
The destructor will close the primary and secondary file descriptor/HANDLEs if they are valid and own...
#define UNUSED_IF_ASSERT_DISABLED(x)
A class that represents a running process on the host machine.
char * ptsname(int fd)
int grantpt(int fd)
pid_t setsid(void)
int posix_openpt(int flag)
pid_t fork(void)
int unlockpt(int fd)
#define PATH_MAX