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