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
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.
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 = 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 
103 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   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