xref: /freebsd/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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      UNUSED_IF_ASSERT_DISABLED(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