brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.3 KiB · 436dfd8 Raw
475 lines · cpp
1//===-- Terminal.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/Terminal.h"10 11#include "lldb/Host/Config.h"12#include "lldb/Host/PosixApi.h"13#include "llvm/ADT/STLExtras.h"14 15#include <csignal>16#include <fcntl.h>17#include <optional>18 19#if LLDB_ENABLE_TERMIOS20#include <termios.h>21#endif22 23using namespace lldb_private;24 25struct Terminal::Data {26#if LLDB_ENABLE_TERMIOS27  struct termios m_termios; ///< Cached terminal state information.28#endif29};30 31bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }32 33#if !LLDB_ENABLE_TERMIOS34static llvm::Error termiosMissingError() {35  return llvm::createStringError(llvm::inconvertibleErrorCode(),36                                 "termios support missing in LLDB");37}38#endif39 40llvm::Expected<Terminal::Data> Terminal::GetData() {41#if LLDB_ENABLE_TERMIOS42  if (!FileDescriptorIsValid())43    return llvm::createStringError(llvm::inconvertibleErrorCode(),44                                   "invalid fd");45 46  if (!IsATerminal())47    return llvm::createStringError(llvm::inconvertibleErrorCode(),48                                   "fd not a terminal");49 50  Data data;51  if (::tcgetattr(m_fd, &data.m_termios) != 0)52    return llvm::createStringError(53        std::error_code(errno, std::generic_category()),54        "unable to get teletype attributes");55  return data;56#else // !LLDB_ENABLE_TERMIOS57  return termiosMissingError();58#endif // LLDB_ENABLE_TERMIOS59}60 61llvm::Error Terminal::SetData(const Terminal::Data &data) {62#if LLDB_ENABLE_TERMIOS63  assert(FileDescriptorIsValid());64  assert(IsATerminal());65 66  if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)67    return llvm::createStringError(68        std::error_code(errno, std::generic_category()),69        "unable to set teletype attributes");70  return llvm::Error::success();71#else // !LLDB_ENABLE_TERMIOS72  return termiosMissingError();73#endif // LLDB_ENABLE_TERMIOS74}75 76llvm::Error Terminal::SetEcho(bool enabled) {77#if LLDB_ENABLE_TERMIOS78  llvm::Expected<Data> data = GetData();79  if (!data)80    return data.takeError();81 82  struct termios &fd_termios = data->m_termios;83  fd_termios.c_lflag &= ~ECHO;84  if (enabled)85    fd_termios.c_lflag |= ECHO;86  return SetData(data.get());87#else // !LLDB_ENABLE_TERMIOS88  return termiosMissingError();89#endif // LLDB_ENABLE_TERMIOS90}91 92llvm::Error Terminal::SetCanonical(bool enabled) {93#if LLDB_ENABLE_TERMIOS94  llvm::Expected<Data> data = GetData();95  if (!data)96    return data.takeError();97 98  struct termios &fd_termios = data->m_termios;99  fd_termios.c_lflag &= ~ICANON;100  if (enabled)101    fd_termios.c_lflag |= ICANON;102  return SetData(data.get());103#else // !LLDB_ENABLE_TERMIOS104  return termiosMissingError();105#endif // LLDB_ENABLE_TERMIOS106}107 108llvm::Error Terminal::SetRaw() {109#if LLDB_ENABLE_TERMIOS110  llvm::Expected<Data> data = GetData();111  if (!data)112    return data.takeError();113 114  struct termios &fd_termios = data->m_termios;115  ::cfmakeraw(&fd_termios);116 117  // Make sure only one character is needed to return from a read118  // (cfmakeraw() doesn't do this on NetBSD)119  fd_termios.c_cc[VMIN] = 1;120  fd_termios.c_cc[VTIME] = 0;121 122  return SetData(data.get());123#else // !LLDB_ENABLE_TERMIOS124  return termiosMissingError();125#endif // LLDB_ENABLE_TERMIOS126}127 128#if LLDB_ENABLE_TERMIOS129static std::optional<speed_t> baudRateToConst(unsigned int baud_rate) {130  switch (baud_rate) {131#if defined(B50)132  case 50:133    return B50;134#endif135#if defined(B75)136  case 75:137    return B75;138#endif139#if defined(B110)140  case 110:141    return B110;142#endif143#if defined(B134)144  case 134:145    return B134;146#endif147#if defined(B150)148  case 150:149    return B150;150#endif151#if defined(B200)152  case 200:153    return B200;154#endif155#if defined(B300)156  case 300:157    return B300;158#endif159#if defined(B600)160  case 600:161    return B600;162#endif163#if defined(B1200)164  case 1200:165    return B1200;166#endif167#if defined(B1800)168  case 1800:169    return B1800;170#endif171#if defined(B2400)172  case 2400:173    return B2400;174#endif175#if defined(B4800)176  case 4800:177    return B4800;178#endif179#if defined(B9600)180  case 9600:181    return B9600;182#endif183#if defined(B19200)184  case 19200:185    return B19200;186#endif187#if defined(B38400)188  case 38400:189    return B38400;190#endif191#if defined(B57600)192  case 57600:193    return B57600;194#endif195#if defined(B115200)196  case 115200:197    return B115200;198#endif199#if defined(B230400)200  case 230400:201    return B230400;202#endif203#if defined(B460800)204  case 460800:205    return B460800;206#endif207#if defined(B500000)208  case 500000:209    return B500000;210#endif211#if defined(B576000)212  case 576000:213    return B576000;214#endif215#if defined(B921600)216  case 921600:217    return B921600;218#endif219#if defined(B1000000)220  case 1000000:221    return B1000000;222#endif223#if defined(B1152000)224  case 1152000:225    return B1152000;226#endif227#if defined(B1500000)228  case 1500000:229    return B1500000;230#endif231#if defined(B2000000)232  case 2000000:233    return B2000000;234#endif235#if defined(B76800)236  case 76800:237    return B76800;238#endif239#if defined(B153600)240  case 153600:241    return B153600;242#endif243#if defined(B307200)244  case 307200:245    return B307200;246#endif247#if defined(B614400)248  case 614400:249    return B614400;250#endif251#if defined(B2500000)252  case 2500000:253    return B2500000;254#endif255#if defined(B3000000)256  case 3000000:257    return B3000000;258#endif259#if defined(B3500000)260  case 3500000:261    return B3500000;262#endif263#if defined(B4000000)264  case 4000000:265    return B4000000;266#endif267  default:268    return std::nullopt;269  }270}271#endif272 273llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {274#if LLDB_ENABLE_TERMIOS275  llvm::Expected<Data> data = GetData();276  if (!data)277    return data.takeError();278 279  struct termios &fd_termios = data->m_termios;280  std::optional<speed_t> val = baudRateToConst(baud_rate);281  if (!val) // invalid value282    return llvm::createStringError(llvm::inconvertibleErrorCode(),283                                   "baud rate %d unsupported by the platform",284                                   baud_rate);285  if (::cfsetispeed(&fd_termios, *val) != 0)286    return llvm::createStringError(287        std::error_code(errno, std::generic_category()),288        "setting input baud rate failed");289  if (::cfsetospeed(&fd_termios, *val) != 0)290    return llvm::createStringError(291        std::error_code(errno, std::generic_category()),292        "setting output baud rate failed");293  return SetData(data.get());294#else // !LLDB_ENABLE_TERMIOS295  return termiosMissingError();296#endif // LLDB_ENABLE_TERMIOS297}298 299llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {300#if LLDB_ENABLE_TERMIOS301  llvm::Expected<Data> data = GetData();302  if (!data)303    return data.takeError();304 305  struct termios &fd_termios = data->m_termios;306  switch (stop_bits) {307  case 1:308    fd_termios.c_cflag &= ~CSTOPB;309    break;310  case 2:311    fd_termios.c_cflag |= CSTOPB;312    break;313  default:314    return llvm::createStringError(315        llvm::inconvertibleErrorCode(),316        "invalid stop bit count: %d (must be 1 or 2)", stop_bits);317  }318  return SetData(data.get());319#else // !LLDB_ENABLE_TERMIOS320  return termiosMissingError();321#endif // LLDB_ENABLE_TERMIOS322}323 324llvm::Error Terminal::SetParity(Terminal::Parity parity) {325#if LLDB_ENABLE_TERMIOS326  llvm::Expected<Data> data = GetData();327  if (!data)328    return data.takeError();329 330  struct termios &fd_termios = data->m_termios;331  fd_termios.c_cflag &= ~(332#if defined(CMSPAR)333      CMSPAR |334#endif335      PARENB | PARODD);336 337  if (parity != Parity::No) {338    fd_termios.c_cflag |= PARENB;339    if (parity == Parity::Odd || parity == Parity::Mark)340      fd_termios.c_cflag |= PARODD;341    if (parity == Parity::Mark || parity == Parity::Space) {342#if defined(CMSPAR)343      fd_termios.c_cflag |= CMSPAR;344#else345      return llvm::createStringError(346          llvm::inconvertibleErrorCode(),347          "space/mark parity is not supported by the platform");348#endif349    }350  }351  return SetData(data.get());352#else // !LLDB_ENABLE_TERMIOS353  return termiosMissingError();354#endif // LLDB_ENABLE_TERMIOS355}356 357llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {358#if LLDB_ENABLE_TERMIOS359  llvm::Expected<Data> data = GetData();360  if (!data)361    return data.takeError();362 363  struct termios &fd_termios = data->m_termios;364  fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);365 366  if (parity_check != ParityCheck::No) {367    fd_termios.c_iflag |= INPCK;368    if (parity_check == ParityCheck::Ignore)369      fd_termios.c_iflag |= IGNPAR;370    else if (parity_check == ParityCheck::Mark)371      fd_termios.c_iflag |= PARMRK;372  }373  return SetData(data.get());374#else // !LLDB_ENABLE_TERMIOS375  return termiosMissingError();376#endif // LLDB_ENABLE_TERMIOS377}378 379llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {380#if LLDB_ENABLE_TERMIOS381  llvm::Expected<Data> data = GetData();382  if (!data)383    return data.takeError();384 385#if defined(CRTSCTS)386  struct termios &fd_termios = data->m_termios;387  fd_termios.c_cflag &= ~CRTSCTS;388  if (enabled)389    fd_termios.c_cflag |= CRTSCTS;390  return SetData(data.get());391#else  // !defined(CRTSCTS)392  if (enabled)393    return llvm::createStringError(394        llvm::inconvertibleErrorCode(),395        "hardware flow control is not supported by the platform");396  return llvm::Error::success();397#endif // defined(CRTSCTS)398#else // !LLDB_ENABLE_TERMIOS399  return termiosMissingError();400#endif // LLDB_ENABLE_TERMIOS401}402 403TerminalState::TerminalState(Terminal term, bool save_process_group)404    : m_tty(term) {405  Save(term, save_process_group);406}407 408TerminalState::~TerminalState() { Restore(); }409 410void TerminalState::Clear() {411  m_tty.Clear();412  m_tflags = -1;413  m_data.reset();414  m_process_group = -1;415}416 417bool TerminalState::Save(Terminal term, bool save_process_group) {418  Clear();419  m_tty = term;420  if (m_tty.IsATerminal()) {421#if LLDB_ENABLE_POSIX422    int fd = m_tty.GetFileDescriptor();423    m_tflags = ::fcntl(fd, F_GETFL, 0);424#if LLDB_ENABLE_TERMIOS425    std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};426    if (::tcgetattr(fd, &new_data->m_termios) == 0)427      m_data = std::move(new_data);428#endif // LLDB_ENABLE_TERMIOS429    if (save_process_group)430      m_process_group = ::tcgetpgrp(fd);431#endif // LLDB_ENABLE_POSIX432  }433  return IsValid();434}435 436bool TerminalState::Restore() const {437#if LLDB_ENABLE_POSIX438  if (IsValid()) {439    const int fd = m_tty.GetFileDescriptor();440    if (TFlagsIsValid())441      fcntl(fd, F_SETFL, m_tflags);442 443#if LLDB_ENABLE_TERMIOS444    if (TTYStateIsValid())445      tcsetattr(fd, TCSANOW, &m_data->m_termios);446#endif // LLDB_ENABLE_TERMIOS447 448    if (ProcessGroupIsValid()) {449      // Save the original signal handler.450      void (*saved_sigttou_callback)(int) = nullptr;451      saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);452      // Set the process group453      tcsetpgrp(fd, m_process_group);454      // Restore the original signal handler.455      signal(SIGTTOU, saved_sigttou_callback);456    }457    return true;458  }459#endif // LLDB_ENABLE_POSIX460  return false;461}462 463bool TerminalState::IsValid() const {464  return m_tty.FileDescriptorIsValid() &&465         (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());466}467 468bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }469 470bool TerminalState::TTYStateIsValid() const { return bool(m_data); }471 472bool TerminalState::ProcessGroupIsValid() const {473  return static_cast<int32_t>(m_process_group) != -1;474}475