LLDB mainline
PseudoTerminal.cpp
Go to the documentation of this file.
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
10#include "lldb/Host/Config.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__)
31int posix_openpt(int flags);
32#endif
33
34using namespace lldb_private;
35
36// PseudoTerminal constructor
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.
48}
49
50// Close the primary file descriptor if it is valid.
52 if (m_primary_fd >= 0) {
53 ::close(m_primary_fd);
55 }
56}
57
58// Close the secondary file descriptor if it is valid.
60 if (m_secondary_fd >= 0) {
61 ::close(m_secondary_fd);
63 }
64}
65
67#if LLDB_ENABLE_POSIX
68 // Open the primary side of a pseudo terminal
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());
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());
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
95llvm::Error PseudoTerminal::OpenSecondary(int oflag) {
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__)
108static 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
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));
127 assert(r == 0);
128 return buf;
129#if defined(__APPLE__)
130 } else {
132 }
133#endif
134#else
136#endif
137}
138
139llvm::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...
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.
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.
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.
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;
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.
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;
220 return fd;
221}
static std::string use_ptsname(int fd)
int Open(const char *path, int flags, int mode=0600)
Wraps ::open in a platform-independent way.
static FileSystem & Instance()
PseudoTerminal()
Default constructor.
int m_secondary_fd
The file descriptor for the secondary.
int ReleaseSecondaryFileDescriptor()
Release the secondary file descriptor.
llvm::Error OpenFirstAvailablePrimary(int oflag)
Open the first available pseudo terminal.
llvm::Error OpenSecondary(int oflag)
Open the secondary for the current primary pseudo terminal.
void ClosePrimaryFileDescriptor()
Close the primary file descriptor if it is valid.
int m_primary_fd
The file descriptor for the primary.
llvm::Expected< lldb::pid_t > Fork()
Fork a child process that uses pseudo terminals for its stdio.
int GetPrimaryFileDescriptor() const
The primary file descriptor accessor.
int ReleasePrimaryFileDescriptor()
Release the primary file descriptor.
std::string GetSecondaryName() const
Get the name of the secondary pseudo terminal.
void CloseSecondaryFileDescriptor()
Close the secondary file descriptor if it is valid.
int GetSecondaryFileDescriptor() const
The secondary file descriptor accessor.
@ invalid_fd
Invalid file descriptor value.
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:140
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
char * ptsname(int fd)
int grantpt(int fd)
pid_t setsid(void)
int posix_openpt(int flag)
pid_t fork(void)
int unlockpt(int fd)
#define PATH_MAX