Path: blob/main/contrib/llvm-project/lldb/source/Host/common/PseudoTerminal.cpp
39607 views
//===-- PseudoTerminal.cpp ------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/PseudoTerminal.h"9#include "lldb/Host/Config.h"10#include "lldb/Host/FileSystem.h"11#include "llvm/Support/Errc.h"12#include "llvm/Support/Errno.h"13#include <cassert>14#include <climits>15#include <cstdio>16#include <cstdlib>17#include <cstring>18#include <mutex>19#if defined(TIOCSCTTY)20#include <sys/ioctl.h>21#endif2223#include "lldb/Host/PosixApi.h"2425#if defined(__APPLE__)26#include <Availability.h>27#endif2829#if defined(__ANDROID__)30int posix_openpt(int flags);31#endif3233using namespace lldb_private;3435// PseudoTerminal constructor36PseudoTerminal::PseudoTerminal() = default;3738// Destructor39//40// The destructor will close the primary and secondary file descriptors if they41// are valid and ownership has not been released using the42// ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member43// functions.44PseudoTerminal::~PseudoTerminal() {45ClosePrimaryFileDescriptor();46CloseSecondaryFileDescriptor();47}4849// Close the primary file descriptor if it is valid.50void PseudoTerminal::ClosePrimaryFileDescriptor() {51if (m_primary_fd >= 0) {52::close(m_primary_fd);53m_primary_fd = invalid_fd;54}55}5657// Close the secondary file descriptor if it is valid.58void PseudoTerminal::CloseSecondaryFileDescriptor() {59if (m_secondary_fd >= 0) {60::close(m_secondary_fd);61m_secondary_fd = invalid_fd;62}63}6465llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {66#if LLDB_ENABLE_POSIX67// Open the primary side of a pseudo terminal68m_primary_fd = ::posix_openpt(oflag);69if (m_primary_fd < 0) {70return llvm::errorCodeToError(71std::error_code(errno, std::generic_category()));72}7374// Grant access to the secondary pseudo terminal75if (::grantpt(m_primary_fd) < 0) {76std::error_code EC(errno, std::generic_category());77ClosePrimaryFileDescriptor();78return llvm::errorCodeToError(EC);79}8081// Clear the lock flag on the secondary pseudo terminal82if (::unlockpt(m_primary_fd) < 0) {83std::error_code EC(errno, std::generic_category());84ClosePrimaryFileDescriptor();85return llvm::errorCodeToError(EC);86}8788return llvm::Error::success();89#else90return llvm::errorCodeToError(llvm::errc::not_supported);91#endif92}9394llvm::Error PseudoTerminal::OpenSecondary(int oflag) {95CloseSecondaryFileDescriptor();9697std::string name = GetSecondaryName();98m_secondary_fd = FileSystem::Instance().Open(name.c_str(), oflag);99if (m_secondary_fd >= 0)100return llvm::Error::success();101102return llvm::errorCodeToError(103std::error_code(errno, std::generic_category()));104}105106#if !HAVE_PTSNAME_R || defined(__APPLE__)107static std::string use_ptsname(int fd) {108static std::mutex mutex;109std::lock_guard<std::mutex> guard(mutex);110const char *r = ptsname(fd);111assert(r != nullptr);112return r;113}114#endif115116std::string PseudoTerminal::GetSecondaryName() const {117assert(m_primary_fd >= 0);118#if HAVE_PTSNAME_R119#if defined(__APPLE__)120if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {121#endif122char buf[PATH_MAX];123buf[0] = '\0';124int r = ptsname_r(m_primary_fd, buf, sizeof(buf));125UNUSED_IF_ASSERT_DISABLED(r);126assert(r == 0);127return buf;128#if defined(__APPLE__)129} else {130return use_ptsname(m_primary_fd);131}132#endif133#else134return use_ptsname(m_primary_fd);135#endif136}137138llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {139#if LLDB_ENABLE_POSIX140if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))141return std::move(Err);142143pid_t pid = ::fork();144if (pid < 0) {145return llvm::errorCodeToError(146std::error_code(errno, std::generic_category()));147}148if (pid > 0) {149// Parent process.150return pid;151}152153// Child Process154::setsid();155156if (llvm::Error Err = OpenSecondary(O_RDWR))157return std::move(Err);158159// Primary FD should have O_CLOEXEC set, but let's close it just in160// case...161ClosePrimaryFileDescriptor();162163#if defined(TIOCSCTTY)164// Acquire the controlling terminal165if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {166return llvm::errorCodeToError(167std::error_code(errno, std::generic_category()));168}169#endif170// Duplicate all stdio file descriptors to the secondary pseudo terminal171for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {172if (::dup2(m_secondary_fd, fd) != fd) {173return llvm::errorCodeToError(174std::error_code(errno, std::generic_category()));175}176}177#endif178return 0;179}180181// The primary file descriptor accessor. This object retains ownership of the182// primary file descriptor when this accessor is used. Use183// ReleasePrimaryFileDescriptor() if you wish this object to release ownership184// of the primary file descriptor.185//186// Returns the primary file descriptor, or -1 if the primary file descriptor is187// not currently valid.188int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }189190// The secondary file descriptor accessor.191//192// Returns the secondary file descriptor, or -1 if the secondary file descriptor193// is not currently valid.194int PseudoTerminal::GetSecondaryFileDescriptor() const {195return m_secondary_fd;196}197198// Release ownership of the primary pseudo terminal file descriptor without199// closing it. The destructor for this class will close the primary file200// descriptor if the ownership isn't released using this call and the primary201// file descriptor has been opened.202int PseudoTerminal::ReleasePrimaryFileDescriptor() {203// Release ownership of the primary pseudo terminal file descriptor without204// closing it. (the destructor for this class will close it otherwise!)205int fd = m_primary_fd;206m_primary_fd = invalid_fd;207return fd;208}209210// Release ownership of the secondary pseudo terminal file descriptor without211// closing it. The destructor for this class will close the secondary file212// descriptor if the ownership isn't released using this call and the secondary213// file descriptor has been opened.214int PseudoTerminal::ReleaseSecondaryFileDescriptor() {215// Release ownership of the secondary pseudo terminal file descriptor without216// closing it (the destructor for this class will close it otherwise!)217int fd = m_secondary_fd;218m_secondary_fd = invalid_fd;219return fd;220}221222223