• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <limits.h>
15 #include <mutex>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
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
PseudoTerminal()32 PseudoTerminal::PseudoTerminal()
33     : m_primary_fd(invalid_fd), m_secondary_fd(invalid_fd) {}
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.
~PseudoTerminal()41 PseudoTerminal::~PseudoTerminal() {
42   ClosePrimaryFileDescriptor();
43   CloseSecondaryFileDescriptor();
44 }
45 
46 // Close the primary file descriptor if it is valid.
ClosePrimaryFileDescriptor()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.
CloseSecondaryFileDescriptor()55 void PseudoTerminal::CloseSecondaryFileDescriptor() {
56   if (m_secondary_fd >= 0) {
57     ::close(m_secondary_fd);
58     m_secondary_fd = invalid_fd;
59   }
60 }
61 
OpenFirstAvailablePrimary(int oflag)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 
OpenSecondary(int oflag)91 llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
92   CloseSecondaryFileDescriptor();
93 
94   std::string name = GetSecondaryName();
95   m_secondary_fd = llvm::sys::RetryAfterSignal(-1, ::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 
GetSecondaryName() const103 std::string PseudoTerminal::GetSecondaryName() const {
104   assert(m_primary_fd >= 0);
105 #if HAVE_PTSNAME_R
106   char buf[PATH_MAX];
107   buf[0] = '\0';
108   int r = ptsname_r(m_primary_fd, buf, sizeof(buf));
109   (void)r;
110   assert(r == 0);
111   return buf;
112 #else
113   static std::mutex mutex;
114   std::lock_guard<std::mutex> guard(mutex);
115   const char *r = ptsname(m_primary_fd);
116   assert(r != nullptr);
117   return r;
118 #endif
119 }
120 
Fork()121 llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {
122 #if LLDB_ENABLE_POSIX
123   if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))
124     return std::move(Err);
125 
126   pid_t pid = ::fork();
127   if (pid < 0) {
128     return llvm::errorCodeToError(
129         std::error_code(errno, std::generic_category()));
130   }
131   if (pid > 0) {
132     // Parent process.
133     return pid;
134   }
135 
136   // Child Process
137   ::setsid();
138 
139   if (llvm::Error Err = OpenSecondary(O_RDWR))
140     return std::move(Err);
141 
142   // Primary FD should have O_CLOEXEC set, but let's close it just in
143   // case...
144   ClosePrimaryFileDescriptor();
145 
146 #if defined(TIOCSCTTY)
147   // Acquire the controlling terminal
148   if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {
149     return llvm::errorCodeToError(
150         std::error_code(errno, std::generic_category()));
151   }
152 #endif
153   // Duplicate all stdio file descriptors to the secondary pseudo terminal
154   for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {
155     if (::dup2(m_secondary_fd, fd) != fd) {
156       return llvm::errorCodeToError(
157           std::error_code(errno, std::generic_category()));
158     }
159   }
160 #endif
161   return 0;
162 }
163 
164 // The primary file descriptor accessor. This object retains ownership of the
165 // primary file descriptor when this accessor is used. Use
166 // ReleasePrimaryFileDescriptor() if you wish this object to release ownership
167 // of the primary file descriptor.
168 //
169 // Returns the primary file descriptor, or -1 if the primary file descriptor is
170 // not currently valid.
GetPrimaryFileDescriptor() const171 int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }
172 
173 // The secondary file descriptor accessor.
174 //
175 // Returns the secondary file descriptor, or -1 if the secondary file descriptor
176 // is not currently valid.
GetSecondaryFileDescriptor() const177 int PseudoTerminal::GetSecondaryFileDescriptor() const {
178   return m_secondary_fd;
179 }
180 
181 // Release ownership of the primary pseudo terminal file descriptor without
182 // closing it. The destructor for this class will close the primary file
183 // descriptor if the ownership isn't released using this call and the primary
184 // file descriptor has been opened.
ReleasePrimaryFileDescriptor()185 int PseudoTerminal::ReleasePrimaryFileDescriptor() {
186   // Release ownership of the primary pseudo terminal file descriptor without
187   // closing it. (the destructor for this class will close it otherwise!)
188   int fd = m_primary_fd;
189   m_primary_fd = invalid_fd;
190   return fd;
191 }
192 
193 // Release ownership of the secondary pseudo terminal file descriptor without
194 // closing it. The destructor for this class will close the secondary file
195 // descriptor if the ownership isn't released using this call and the secondary
196 // file descriptor has been opened.
ReleaseSecondaryFileDescriptor()197 int PseudoTerminal::ReleaseSecondaryFileDescriptor() {
198   // Release ownership of the secondary pseudo terminal file descriptor without
199   // closing it (the destructor for this class will close it otherwise!)
200   int fd = m_secondary_fd;
201   m_secondary_fd = invalid_fd;
202   return fd;
203 }
204