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