xref: /freebsd-src/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp (revision 5956d97f4b3204318ceb6aa9c77bd0bc6ea87a41)
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.
40 PseudoTerminal::~PseudoTerminal() {
41   ClosePrimaryFileDescriptor();
42   CloseSecondaryFileDescriptor();
43 }
44 
45 // Close the primary file descriptor if it is valid.
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.
54 void PseudoTerminal::CloseSecondaryFileDescriptor() {
55   if (m_secondary_fd >= 0) {
56     ::close(m_secondary_fd);
57     m_secondary_fd = invalid_fd;
58   }
59 }
60 
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 
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 
102 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 
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.
170 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.
176 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.
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.
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