xref: /openbsd-src/gnu/llvm/lldb/source/Host/common/PseudoTerminal.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1dda28197Spatrick //===-- PseudoTerminal.cpp ------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Host/PseudoTerminal.h"
10061da546Spatrick #include "lldb/Host/Config.h"
11be691f3bSpatrick #include "llvm/Support/Errc.h"
12061da546Spatrick #include "llvm/Support/Errno.h"
13be691f3bSpatrick #include <cassert>
14be691f3bSpatrick #include <climits>
15be691f3bSpatrick #include <cstdio>
16be691f3bSpatrick #include <cstdlib>
17be691f3bSpatrick #include <cstring>
18be691f3bSpatrick #include <mutex>
19061da546Spatrick #if defined(TIOCSCTTY)
20061da546Spatrick #include <sys/ioctl.h>
21061da546Spatrick #endif
22061da546Spatrick 
23061da546Spatrick #include "lldb/Host/PosixApi.h"
24061da546Spatrick 
25*f6aab3d8Srobert #if defined(__APPLE__)
26*f6aab3d8Srobert #include <Availability.h>
27*f6aab3d8Srobert #endif
28*f6aab3d8Srobert 
29061da546Spatrick #if defined(__ANDROID__)
30061da546Spatrick int posix_openpt(int flags);
31061da546Spatrick #endif
32061da546Spatrick 
33061da546Spatrick using namespace lldb_private;
34061da546Spatrick 
35061da546Spatrick // PseudoTerminal constructor
36be691f3bSpatrick PseudoTerminal::PseudoTerminal() = default;
37061da546Spatrick 
38061da546Spatrick // Destructor
39061da546Spatrick //
40dda28197Spatrick // The destructor will close the primary and secondary file descriptors if they
41dda28197Spatrick // are valid and ownership has not been released using the
42dda28197Spatrick // ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member
43061da546Spatrick // functions.
~PseudoTerminal()44061da546Spatrick PseudoTerminal::~PseudoTerminal() {
45dda28197Spatrick   ClosePrimaryFileDescriptor();
46dda28197Spatrick   CloseSecondaryFileDescriptor();
47061da546Spatrick }
48061da546Spatrick 
49dda28197Spatrick // Close the primary file descriptor if it is valid.
ClosePrimaryFileDescriptor()50dda28197Spatrick void PseudoTerminal::ClosePrimaryFileDescriptor() {
51dda28197Spatrick   if (m_primary_fd >= 0) {
52dda28197Spatrick     ::close(m_primary_fd);
53dda28197Spatrick     m_primary_fd = invalid_fd;
54061da546Spatrick   }
55061da546Spatrick }
56061da546Spatrick 
57dda28197Spatrick // Close the secondary file descriptor if it is valid.
CloseSecondaryFileDescriptor()58dda28197Spatrick void PseudoTerminal::CloseSecondaryFileDescriptor() {
59dda28197Spatrick   if (m_secondary_fd >= 0) {
60dda28197Spatrick     ::close(m_secondary_fd);
61dda28197Spatrick     m_secondary_fd = invalid_fd;
62061da546Spatrick   }
63061da546Spatrick }
64061da546Spatrick 
OpenFirstAvailablePrimary(int oflag)65be691f3bSpatrick llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
66061da546Spatrick #if LLDB_ENABLE_POSIX
67dda28197Spatrick   // Open the primary side of a pseudo terminal
68dda28197Spatrick   m_primary_fd = ::posix_openpt(oflag);
69dda28197Spatrick   if (m_primary_fd < 0) {
70be691f3bSpatrick     return llvm::errorCodeToError(
71be691f3bSpatrick         std::error_code(errno, std::generic_category()));
72061da546Spatrick   }
73061da546Spatrick 
74dda28197Spatrick   // Grant access to the secondary pseudo terminal
75dda28197Spatrick   if (::grantpt(m_primary_fd) < 0) {
76be691f3bSpatrick     std::error_code EC(errno, std::generic_category());
77dda28197Spatrick     ClosePrimaryFileDescriptor();
78be691f3bSpatrick     return llvm::errorCodeToError(EC);
79061da546Spatrick   }
80061da546Spatrick 
81dda28197Spatrick   // Clear the lock flag on the secondary pseudo terminal
82dda28197Spatrick   if (::unlockpt(m_primary_fd) < 0) {
83be691f3bSpatrick     std::error_code EC(errno, std::generic_category());
84dda28197Spatrick     ClosePrimaryFileDescriptor();
85be691f3bSpatrick     return llvm::errorCodeToError(EC);
86061da546Spatrick   }
87061da546Spatrick 
88be691f3bSpatrick   return llvm::Error::success();
89061da546Spatrick #else
90be691f3bSpatrick   return llvm::errorCodeToError(llvm::errc::not_supported);
91061da546Spatrick #endif
92061da546Spatrick }
93061da546Spatrick 
OpenSecondary(int oflag)94be691f3bSpatrick llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
95dda28197Spatrick   CloseSecondaryFileDescriptor();
96061da546Spatrick 
97be691f3bSpatrick   std::string name = GetSecondaryName();
98be691f3bSpatrick   m_secondary_fd = llvm::sys::RetryAfterSignal(-1, ::open, name.c_str(), oflag);
99be691f3bSpatrick   if (m_secondary_fd >= 0)
100be691f3bSpatrick     return llvm::Error::success();
101061da546Spatrick 
102be691f3bSpatrick   return llvm::errorCodeToError(
103be691f3bSpatrick       std::error_code(errno, std::generic_category()));
104061da546Spatrick }
105061da546Spatrick 
106*f6aab3d8Srobert #if !HAVE_PTSNAME_R || defined(__APPLE__)
use_ptsname(int fd)107*f6aab3d8Srobert static std::string use_ptsname(int fd) {
108*f6aab3d8Srobert   static std::mutex mutex;
109*f6aab3d8Srobert   std::lock_guard<std::mutex> guard(mutex);
110*f6aab3d8Srobert   const char *r = ptsname(fd);
111*f6aab3d8Srobert   assert(r != nullptr);
112*f6aab3d8Srobert   return r;
113*f6aab3d8Srobert }
114*f6aab3d8Srobert #endif
115*f6aab3d8Srobert 
GetSecondaryName() const116be691f3bSpatrick std::string PseudoTerminal::GetSecondaryName() const {
117be691f3bSpatrick   assert(m_primary_fd >= 0);
118be691f3bSpatrick #if HAVE_PTSNAME_R
119*f6aab3d8Srobert #if defined(__APPLE__)
120*f6aab3d8Srobert   if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {
121*f6aab3d8Srobert #endif
122be691f3bSpatrick     char buf[PATH_MAX];
123be691f3bSpatrick     buf[0] = '\0';
124be691f3bSpatrick     int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
125be691f3bSpatrick     (void)r;
126be691f3bSpatrick     assert(r == 0);
127be691f3bSpatrick     return buf;
128*f6aab3d8Srobert #if defined(__APPLE__)
129*f6aab3d8Srobert   } else {
130*f6aab3d8Srobert     return use_ptsname(m_primary_fd);
131*f6aab3d8Srobert   }
132*f6aab3d8Srobert #endif
133be691f3bSpatrick #else
134*f6aab3d8Srobert   return use_ptsname(m_primary_fd);
135be691f3bSpatrick #endif
136061da546Spatrick }
137061da546Spatrick 
Fork()138be691f3bSpatrick llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
139061da546Spatrick #if LLDB_ENABLE_POSIX
140be691f3bSpatrick   if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
141be691f3bSpatrick     return std::move(Err);
142061da546Spatrick 
143be691f3bSpatrick   pid_t pid = ::fork();
144061da546Spatrick   if (pid < 0) {
145be691f3bSpatrick     return llvm::errorCodeToError(
146be691f3bSpatrick         std::error_code(errno, std::generic_category()));
147be691f3bSpatrick   }
148be691f3bSpatrick   if (pid > 0) {
149be691f3bSpatrick     // Parent process.
150be691f3bSpatrick     return pid;
151be691f3bSpatrick   }
152be691f3bSpatrick 
153061da546Spatrick   // Child Process
154061da546Spatrick   ::setsid();
155061da546Spatrick 
156be691f3bSpatrick   if (llvm::Error Err = OpenSecondary(O_RDWR))
157be691f3bSpatrick     return std::move(Err);
158061da546Spatrick 
159dda28197Spatrick   // Primary FD should have O_CLOEXEC set, but let's close it just in
160061da546Spatrick   // case...
161dda28197Spatrick   ClosePrimaryFileDescriptor();
162061da546Spatrick 
163061da546Spatrick #if defined(TIOCSCTTY)
164061da546Spatrick   // Acquire the controlling terminal
165dda28197Spatrick   if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
166be691f3bSpatrick     return llvm::errorCodeToError(
167be691f3bSpatrick         std::error_code(errno, std::generic_category()));
168061da546Spatrick   }
169061da546Spatrick #endif
170dda28197Spatrick   // Duplicate all stdio file descriptors to the secondary pseudo terminal
171be691f3bSpatrick   for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
172be691f3bSpatrick     if (::dup2(m_secondary_fd, fd) != fd) {
173be691f3bSpatrick       return llvm::errorCodeToError(
174be691f3bSpatrick           std::error_code(errno, std::generic_category()));
175061da546Spatrick     }
176061da546Spatrick   }
177061da546Spatrick #endif
178be691f3bSpatrick   return 0;
179061da546Spatrick }
180061da546Spatrick 
181dda28197Spatrick // The primary file descriptor accessor. This object retains ownership of the
182dda28197Spatrick // primary file descriptor when this accessor is used. Use
183dda28197Spatrick // ReleasePrimaryFileDescriptor() if you wish this object to release ownership
184dda28197Spatrick // of the primary file descriptor.
185061da546Spatrick //
186dda28197Spatrick // Returns the primary file descriptor, or -1 if the primary file descriptor is
187061da546Spatrick // not currently valid.
GetPrimaryFileDescriptor() const188dda28197Spatrick int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
189061da546Spatrick 
190dda28197Spatrick // The secondary file descriptor accessor.
191061da546Spatrick //
192dda28197Spatrick // Returns the secondary file descriptor, or -1 if the secondary file descriptor
193dda28197Spatrick // is not currently valid.
GetSecondaryFileDescriptor() const194dda28197Spatrick int PseudoTerminal::GetSecondaryFileDescriptor() const {
195dda28197Spatrick   return m_secondary_fd;
196dda28197Spatrick }
197061da546Spatrick 
198dda28197Spatrick // Release ownership of the primary pseudo terminal file descriptor without
199dda28197Spatrick // closing it. The destructor for this class will close the primary file
200dda28197Spatrick // descriptor if the ownership isn't released using this call and the primary
201061da546Spatrick // file descriptor has been opened.
ReleasePrimaryFileDescriptor()202dda28197Spatrick int PseudoTerminal::ReleasePrimaryFileDescriptor() {
203dda28197Spatrick   // Release ownership of the primary pseudo terminal file descriptor without
204061da546Spatrick   // closing it. (the destructor for this class will close it otherwise!)
205dda28197Spatrick   int fd = m_primary_fd;
206dda28197Spatrick   m_primary_fd = invalid_fd;
207061da546Spatrick   return fd;
208061da546Spatrick }
209061da546Spatrick 
210dda28197Spatrick // Release ownership of the secondary pseudo terminal file descriptor without
211dda28197Spatrick // closing it. The destructor for this class will close the secondary file
212dda28197Spatrick // descriptor if the ownership isn't released using this call and the secondary
213061da546Spatrick // file descriptor has been opened.
ReleaseSecondaryFileDescriptor()214dda28197Spatrick int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
215dda28197Spatrick   // Release ownership of the secondary pseudo terminal file descriptor without
216061da546Spatrick   // closing it (the destructor for this class will close it otherwise!)
217dda28197Spatrick   int fd = m_secondary_fd;
218dda28197Spatrick   m_secondary_fd = invalid_fd;
219061da546Spatrick   return fd;
220061da546Spatrick }
221