diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp new file mode 100644 index 000000000000..d53327973eb2 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp @@ -0,0 +1,221 @@ +//===-- PseudoTerminal.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSystem.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Errno.h" +#include <cassert> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <mutex> +#if defined(TIOCSCTTY) +#include <sys/ioctl.h> +#endif + +#include "lldb/Host/PosixApi.h" + +#if defined(__APPLE__) +#include <Availability.h> +#endif + +#if defined(__ANDROID__) +int posix_openpt(int flags); +#endif + +using namespace lldb_private; + +// PseudoTerminal constructor +PseudoTerminal::PseudoTerminal() = default; + +// Destructor +// +// The destructor will close the primary and secondary file descriptors if they +// are valid and ownership has not been released using the +// ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member +// functions. +PseudoTerminal::~PseudoTerminal() { + ClosePrimaryFileDescriptor(); + CloseSecondaryFileDescriptor(); +} + +// Close the primary file descriptor if it is valid. +void PseudoTerminal::ClosePrimaryFileDescriptor() { + if (m_primary_fd >= 0) { + ::close(m_primary_fd); + m_primary_fd = invalid_fd; + } +} + +// Close the secondary file descriptor if it is valid. +void PseudoTerminal::CloseSecondaryFileDescriptor() { + if (m_secondary_fd >= 0) { + ::close(m_secondary_fd); + m_secondary_fd = invalid_fd; + } +} + +llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) { +#if LLDB_ENABLE_POSIX + // Open the primary side of a pseudo terminal + m_primary_fd = ::posix_openpt(oflag); + if (m_primary_fd < 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + + // Grant access to the secondary pseudo terminal + if (::grantpt(m_primary_fd) < 0) { + std::error_code EC(errno, std::generic_category()); + ClosePrimaryFileDescriptor(); + return llvm::errorCodeToError(EC); + } + + // Clear the lock flag on the secondary pseudo terminal + if (::unlockpt(m_primary_fd) < 0) { + std::error_code EC(errno, std::generic_category()); + ClosePrimaryFileDescriptor(); + return llvm::errorCodeToError(EC); + } + + return llvm::Error::success(); +#else + return llvm::errorCodeToError(llvm::errc::not_supported); +#endif +} + +llvm::Error PseudoTerminal::OpenSecondary(int oflag) { + CloseSecondaryFileDescriptor(); + + std::string name = GetSecondaryName(); + m_secondary_fd = FileSystem::Instance().Open(name.c_str(), oflag); + if (m_secondary_fd >= 0) + return llvm::Error::success(); + + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); +} + +#if !HAVE_PTSNAME_R || defined(__APPLE__) +static std::string use_ptsname(int fd) { + static std::mutex mutex; + std::lock_guard<std::mutex> guard(mutex); + const char *r = ptsname(fd); + assert(r != nullptr); + return r; +} +#endif + +std::string PseudoTerminal::GetSecondaryName() const { + assert(m_primary_fd >= 0); +#if HAVE_PTSNAME_R +#if defined(__APPLE__) + if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) { +#endif + char buf[PATH_MAX]; + buf[0] = '\0'; + int r = ptsname_r(m_primary_fd, buf, sizeof(buf)); + UNUSED_IF_ASSERT_DISABLED(r); + assert(r == 0); + return buf; +#if defined(__APPLE__) + } else { + return use_ptsname(m_primary_fd); + } +#endif +#else + return use_ptsname(m_primary_fd); +#endif +} + +llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() { +#if LLDB_ENABLE_POSIX + if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC)) + return std::move(Err); + + pid_t pid = ::fork(); + if (pid < 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + if (pid > 0) { + // Parent process. + return pid; + } + + // Child Process + ::setsid(); + + if (llvm::Error Err = OpenSecondary(O_RDWR)) + return std::move(Err); + + // Primary FD should have O_CLOEXEC set, but let's close it just in + // case... + ClosePrimaryFileDescriptor(); + +#if defined(TIOCSCTTY) + // Acquire the controlling terminal + if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } +#endif + // Duplicate all stdio file descriptors to the secondary pseudo terminal + for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) { + if (::dup2(m_secondary_fd, fd) != fd) { + return llvm::errorCodeToError( + std::error_code(errno, std::generic_category())); + } + } +#endif + return 0; +} + +// The primary file descriptor accessor. This object retains ownership of the +// primary file descriptor when this accessor is used. Use +// ReleasePrimaryFileDescriptor() if you wish this object to release ownership +// of the primary file descriptor. +// +// Returns the primary file descriptor, or -1 if the primary file descriptor is +// not currently valid. +int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; } + +// The secondary file descriptor accessor. +// +// Returns the secondary file descriptor, or -1 if the secondary file descriptor +// is not currently valid. +int PseudoTerminal::GetSecondaryFileDescriptor() const { + return m_secondary_fd; +} + +// Release ownership of the primary pseudo terminal file descriptor without +// closing it. The destructor for this class will close the primary file +// descriptor if the ownership isn't released using this call and the primary +// file descriptor has been opened. +int PseudoTerminal::ReleasePrimaryFileDescriptor() { + // Release ownership of the primary pseudo terminal file descriptor without + // closing it. (the destructor for this class will close it otherwise!) + int fd = m_primary_fd; + m_primary_fd = invalid_fd; + return fd; +} + +// Release ownership of the secondary pseudo terminal file descriptor without +// closing it. The destructor for this class will close the secondary file +// descriptor if the ownership isn't released using this call and the secondary +// file descriptor has been opened. +int PseudoTerminal::ReleaseSecondaryFileDescriptor() { + // Release ownership of the secondary pseudo terminal file descriptor without + // closing it (the destructor for this class will close it otherwise!) + int fd = m_secondary_fd; + m_secondary_fd = invalid_fd; + return fd; +} |