brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.4 KiB · 53e91af Raw
218 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/PseudoTerminal.h"10#include "lldb/Host/Config.h"11#include "lldb/Host/FileSystem.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#endif23 24#include "lldb/Host/PosixApi.h"25 26#if defined(__APPLE__)27#include <Availability.h>28#endif29 30using namespace lldb_private;31 32// PseudoTerminal constructor33PseudoTerminal::PseudoTerminal() = default;34 35// Destructor36//37// The destructor will close the primary and secondary file descriptors if they38// are valid and ownership has not been released using the39// ReleasePrimaryFileDescriptor() or the ReleaseSaveFileDescriptor() member40// functions.41PseudoTerminal::~PseudoTerminal() {42  ClosePrimaryFileDescriptor();43  CloseSecondaryFileDescriptor();44}45 46// Close the primary file descriptor if it is valid.47void 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.55void PseudoTerminal::CloseSecondaryFileDescriptor() {56  if (m_secondary_fd >= 0) {57    ::close(m_secondary_fd);58    m_secondary_fd = invalid_fd;59  }60}61 62llvm::Error PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {63#if LLDB_ENABLE_POSIX64  // Open the primary side of a pseudo terminal65  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 terminal72  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 terminal79  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#else87  return llvm::errorCodeToError(llvm::errc::not_supported);88#endif89}90 91llvm::Error PseudoTerminal::OpenSecondary(int oflag) {92  CloseSecondaryFileDescriptor();93 94  std::string name = GetSecondaryName();95  m_secondary_fd = FileSystem::Instance().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#if !HAVE_PTSNAME_R || defined(__APPLE__)104static std::string use_ptsname(int fd) {105  static std::mutex mutex;106  std::lock_guard<std::mutex> guard(mutex);107  const char *r = ptsname(fd);108  assert(r != nullptr);109  return r;110}111#endif112 113std::string PseudoTerminal::GetSecondaryName() const {114  assert(m_primary_fd >= 0);115#if HAVE_PTSNAME_R116#if defined(__APPLE__)117  if (__builtin_available(macos 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.4, *)) {118#endif119    char buf[PATH_MAX];120    buf[0] = '\0';121    int r = ptsname_r(m_primary_fd, buf, sizeof(buf));122    UNUSED_IF_ASSERT_DISABLED(r);123    assert(r == 0);124    return buf;125#if defined(__APPLE__)126  } else {127    return use_ptsname(m_primary_fd);128  }129#endif130#else131  return use_ptsname(m_primary_fd);132#endif133}134 135llvm::Expected<lldb::pid_t> PseudoTerminal::Fork() {136#if LLDB_ENABLE_POSIX137  if (llvm::Error Err = OpenFirstAvailablePrimary(O_RDWR | O_CLOEXEC))138    return std::move(Err);139 140  pid_t pid = ::fork();141  if (pid < 0) {142    return llvm::errorCodeToError(143        std::error_code(errno, std::generic_category()));144  }145  if (pid > 0) {146    // Parent process.147    return pid;148  }149 150  // Child Process151  ::setsid();152 153  if (llvm::Error Err = OpenSecondary(O_RDWR))154    return std::move(Err);155 156  // Primary FD should have O_CLOEXEC set, but let's close it just in157  // case...158  ClosePrimaryFileDescriptor();159 160#if defined(TIOCSCTTY)161  // Acquire the controlling terminal162  if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0) {163    return llvm::errorCodeToError(164        std::error_code(errno, std::generic_category()));165  }166#endif167  // Duplicate all stdio file descriptors to the secondary pseudo terminal168  for (int fd : {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) {169    if (::dup2(m_secondary_fd, fd) != fd) {170      return llvm::errorCodeToError(171          std::error_code(errno, std::generic_category()));172    }173  }174#endif175  return 0;176}177 178// The primary file descriptor accessor. This object retains ownership of the179// primary file descriptor when this accessor is used. Use180// ReleasePrimaryFileDescriptor() if you wish this object to release ownership181// of the primary file descriptor.182//183// Returns the primary file descriptor, or -1 if the primary file descriptor is184// not currently valid.185int PseudoTerminal::GetPrimaryFileDescriptor() const { return m_primary_fd; }186 187// The secondary file descriptor accessor.188//189// Returns the secondary file descriptor, or -1 if the secondary file descriptor190// is not currently valid.191int PseudoTerminal::GetSecondaryFileDescriptor() const {192  return m_secondary_fd;193}194 195// Release ownership of the primary pseudo terminal file descriptor without196// closing it. The destructor for this class will close the primary file197// descriptor if the ownership isn't released using this call and the primary198// file descriptor has been opened.199int PseudoTerminal::ReleasePrimaryFileDescriptor() {200  // Release ownership of the primary pseudo terminal file descriptor without201  // closing it. (the destructor for this class will close it otherwise!)202  int fd = m_primary_fd;203  m_primary_fd = invalid_fd;204  return fd;205}206 207// Release ownership of the secondary pseudo terminal file descriptor without208// closing it. The destructor for this class will close the secondary file209// descriptor if the ownership isn't released using this call and the secondary210// file descriptor has been opened.211int PseudoTerminal::ReleaseSecondaryFileDescriptor() {212  // Release ownership of the secondary pseudo terminal file descriptor without213  // closing it (the destructor for this class will close it otherwise!)214  int fd = m_secondary_fd;215  m_secondary_fd = invalid_fd;216  return fd;217}218