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     (void)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