Path: blob/main/contrib/llvm-project/lldb/source/Host/common/Terminal.cpp
39606 views
//===-- Terminal.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/Terminal.h"910#include "lldb/Host/Config.h"11#include "lldb/Host/PosixApi.h"12#include "llvm/ADT/STLExtras.h"1314#include <csignal>15#include <fcntl.h>16#include <optional>1718#if LLDB_ENABLE_TERMIOS19#include <termios.h>20#endif2122using namespace lldb_private;2324struct Terminal::Data {25#if LLDB_ENABLE_TERMIOS26struct termios m_termios; ///< Cached terminal state information.27#endif28};2930bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }3132#if !LLDB_ENABLE_TERMIOS33static llvm::Error termiosMissingError() {34return llvm::createStringError(llvm::inconvertibleErrorCode(),35"termios support missing in LLDB");36}37#endif3839llvm::Expected<Terminal::Data> Terminal::GetData() {40#if LLDB_ENABLE_TERMIOS41if (!FileDescriptorIsValid())42return llvm::createStringError(llvm::inconvertibleErrorCode(),43"invalid fd");4445if (!IsATerminal())46return llvm::createStringError(llvm::inconvertibleErrorCode(),47"fd not a terminal");4849Data data;50if (::tcgetattr(m_fd, &data.m_termios) != 0)51return llvm::createStringError(52std::error_code(errno, std::generic_category()),53"unable to get teletype attributes");54return data;55#else // !LLDB_ENABLE_TERMIOS56return termiosMissingError();57#endif // LLDB_ENABLE_TERMIOS58}5960llvm::Error Terminal::SetData(const Terminal::Data &data) {61#if LLDB_ENABLE_TERMIOS62assert(FileDescriptorIsValid());63assert(IsATerminal());6465if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)66return llvm::createStringError(67std::error_code(errno, std::generic_category()),68"unable to set teletype attributes");69return llvm::Error::success();70#else // !LLDB_ENABLE_TERMIOS71return termiosMissingError();72#endif // LLDB_ENABLE_TERMIOS73}7475llvm::Error Terminal::SetEcho(bool enabled) {76#if LLDB_ENABLE_TERMIOS77llvm::Expected<Data> data = GetData();78if (!data)79return data.takeError();8081struct termios &fd_termios = data->m_termios;82fd_termios.c_lflag &= ~ECHO;83if (enabled)84fd_termios.c_lflag |= ECHO;85return SetData(data.get());86#else // !LLDB_ENABLE_TERMIOS87return termiosMissingError();88#endif // LLDB_ENABLE_TERMIOS89}9091llvm::Error Terminal::SetCanonical(bool enabled) {92#if LLDB_ENABLE_TERMIOS93llvm::Expected<Data> data = GetData();94if (!data)95return data.takeError();9697struct termios &fd_termios = data->m_termios;98fd_termios.c_lflag &= ~ICANON;99if (enabled)100fd_termios.c_lflag |= ICANON;101return SetData(data.get());102#else // !LLDB_ENABLE_TERMIOS103return termiosMissingError();104#endif // LLDB_ENABLE_TERMIOS105}106107llvm::Error Terminal::SetRaw() {108#if LLDB_ENABLE_TERMIOS109llvm::Expected<Data> data = GetData();110if (!data)111return data.takeError();112113struct termios &fd_termios = data->m_termios;114::cfmakeraw(&fd_termios);115116// Make sure only one character is needed to return from a read117// (cfmakeraw() doesn't do this on NetBSD)118fd_termios.c_cc[VMIN] = 1;119fd_termios.c_cc[VTIME] = 0;120121return SetData(data.get());122#else // !LLDB_ENABLE_TERMIOS123return termiosMissingError();124#endif // LLDB_ENABLE_TERMIOS125}126127#if LLDB_ENABLE_TERMIOS128static std::optional<speed_t> baudRateToConst(unsigned int baud_rate) {129switch (baud_rate) {130#if defined(B50)131case 50:132return B50;133#endif134#if defined(B75)135case 75:136return B75;137#endif138#if defined(B110)139case 110:140return B110;141#endif142#if defined(B134)143case 134:144return B134;145#endif146#if defined(B150)147case 150:148return B150;149#endif150#if defined(B200)151case 200:152return B200;153#endif154#if defined(B300)155case 300:156return B300;157#endif158#if defined(B600)159case 600:160return B600;161#endif162#if defined(B1200)163case 1200:164return B1200;165#endif166#if defined(B1800)167case 1800:168return B1800;169#endif170#if defined(B2400)171case 2400:172return B2400;173#endif174#if defined(B4800)175case 4800:176return B4800;177#endif178#if defined(B9600)179case 9600:180return B9600;181#endif182#if defined(B19200)183case 19200:184return B19200;185#endif186#if defined(B38400)187case 38400:188return B38400;189#endif190#if defined(B57600)191case 57600:192return B57600;193#endif194#if defined(B115200)195case 115200:196return B115200;197#endif198#if defined(B230400)199case 230400:200return B230400;201#endif202#if defined(B460800)203case 460800:204return B460800;205#endif206#if defined(B500000)207case 500000:208return B500000;209#endif210#if defined(B576000)211case 576000:212return B576000;213#endif214#if defined(B921600)215case 921600:216return B921600;217#endif218#if defined(B1000000)219case 1000000:220return B1000000;221#endif222#if defined(B1152000)223case 1152000:224return B1152000;225#endif226#if defined(B1500000)227case 1500000:228return B1500000;229#endif230#if defined(B2000000)231case 2000000:232return B2000000;233#endif234#if defined(B76800)235case 76800:236return B76800;237#endif238#if defined(B153600)239case 153600:240return B153600;241#endif242#if defined(B307200)243case 307200:244return B307200;245#endif246#if defined(B614400)247case 614400:248return B614400;249#endif250#if defined(B2500000)251case 2500000:252return B2500000;253#endif254#if defined(B3000000)255case 3000000:256return B3000000;257#endif258#if defined(B3500000)259case 3500000:260return B3500000;261#endif262#if defined(B4000000)263case 4000000:264return B4000000;265#endif266default:267return std::nullopt;268}269}270#endif271272llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {273#if LLDB_ENABLE_TERMIOS274llvm::Expected<Data> data = GetData();275if (!data)276return data.takeError();277278struct termios &fd_termios = data->m_termios;279std::optional<speed_t> val = baudRateToConst(baud_rate);280if (!val) // invalid value281return llvm::createStringError(llvm::inconvertibleErrorCode(),282"baud rate %d unsupported by the platform",283baud_rate);284if (::cfsetispeed(&fd_termios, *val) != 0)285return llvm::createStringError(286std::error_code(errno, std::generic_category()),287"setting input baud rate failed");288if (::cfsetospeed(&fd_termios, *val) != 0)289return llvm::createStringError(290std::error_code(errno, std::generic_category()),291"setting output baud rate failed");292return SetData(data.get());293#else // !LLDB_ENABLE_TERMIOS294return termiosMissingError();295#endif // LLDB_ENABLE_TERMIOS296}297298llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {299#if LLDB_ENABLE_TERMIOS300llvm::Expected<Data> data = GetData();301if (!data)302return data.takeError();303304struct termios &fd_termios = data->m_termios;305switch (stop_bits) {306case 1:307fd_termios.c_cflag &= ~CSTOPB;308break;309case 2:310fd_termios.c_cflag |= CSTOPB;311break;312default:313return llvm::createStringError(314llvm::inconvertibleErrorCode(),315"invalid stop bit count: %d (must be 1 or 2)", stop_bits);316}317return SetData(data.get());318#else // !LLDB_ENABLE_TERMIOS319return termiosMissingError();320#endif // LLDB_ENABLE_TERMIOS321}322323llvm::Error Terminal::SetParity(Terminal::Parity parity) {324#if LLDB_ENABLE_TERMIOS325llvm::Expected<Data> data = GetData();326if (!data)327return data.takeError();328329struct termios &fd_termios = data->m_termios;330fd_termios.c_cflag &= ~(331#if defined(CMSPAR)332CMSPAR |333#endif334PARENB | PARODD);335336if (parity != Parity::No) {337fd_termios.c_cflag |= PARENB;338if (parity == Parity::Odd || parity == Parity::Mark)339fd_termios.c_cflag |= PARODD;340if (parity == Parity::Mark || parity == Parity::Space) {341#if defined(CMSPAR)342fd_termios.c_cflag |= CMSPAR;343#else344return llvm::createStringError(345llvm::inconvertibleErrorCode(),346"space/mark parity is not supported by the platform");347#endif348}349}350return SetData(data.get());351#else // !LLDB_ENABLE_TERMIOS352return termiosMissingError();353#endif // LLDB_ENABLE_TERMIOS354}355356llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {357#if LLDB_ENABLE_TERMIOS358llvm::Expected<Data> data = GetData();359if (!data)360return data.takeError();361362struct termios &fd_termios = data->m_termios;363fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);364365if (parity_check != ParityCheck::No) {366fd_termios.c_iflag |= INPCK;367if (parity_check == ParityCheck::Ignore)368fd_termios.c_iflag |= IGNPAR;369else if (parity_check == ParityCheck::Mark)370fd_termios.c_iflag |= PARMRK;371}372return SetData(data.get());373#else // !LLDB_ENABLE_TERMIOS374return termiosMissingError();375#endif // LLDB_ENABLE_TERMIOS376}377378llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {379#if LLDB_ENABLE_TERMIOS380llvm::Expected<Data> data = GetData();381if (!data)382return data.takeError();383384#if defined(CRTSCTS)385struct termios &fd_termios = data->m_termios;386fd_termios.c_cflag &= ~CRTSCTS;387if (enabled)388fd_termios.c_cflag |= CRTSCTS;389return SetData(data.get());390#else // !defined(CRTSCTS)391if (enabled)392return llvm::createStringError(393llvm::inconvertibleErrorCode(),394"hardware flow control is not supported by the platform");395return llvm::Error::success();396#endif // defined(CRTSCTS)397#else // !LLDB_ENABLE_TERMIOS398return termiosMissingError();399#endif // LLDB_ENABLE_TERMIOS400}401402TerminalState::TerminalState(Terminal term, bool save_process_group)403: m_tty(term) {404Save(term, save_process_group);405}406407TerminalState::~TerminalState() { Restore(); }408409void TerminalState::Clear() {410m_tty.Clear();411m_tflags = -1;412m_data.reset();413m_process_group = -1;414}415416bool TerminalState::Save(Terminal term, bool save_process_group) {417Clear();418m_tty = term;419if (m_tty.IsATerminal()) {420#if LLDB_ENABLE_POSIX421int fd = m_tty.GetFileDescriptor();422m_tflags = ::fcntl(fd, F_GETFL, 0);423#if LLDB_ENABLE_TERMIOS424std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};425if (::tcgetattr(fd, &new_data->m_termios) == 0)426m_data = std::move(new_data);427#endif // LLDB_ENABLE_TERMIOS428if (save_process_group)429m_process_group = ::tcgetpgrp(fd);430#endif // LLDB_ENABLE_POSIX431}432return IsValid();433}434435bool TerminalState::Restore() const {436#if LLDB_ENABLE_POSIX437if (IsValid()) {438const int fd = m_tty.GetFileDescriptor();439if (TFlagsIsValid())440fcntl(fd, F_SETFL, m_tflags);441442#if LLDB_ENABLE_TERMIOS443if (TTYStateIsValid())444tcsetattr(fd, TCSANOW, &m_data->m_termios);445#endif // LLDB_ENABLE_TERMIOS446447if (ProcessGroupIsValid()) {448// Save the original signal handler.449void (*saved_sigttou_callback)(int) = nullptr;450saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);451// Set the process group452tcsetpgrp(fd, m_process_group);453// Restore the original signal handler.454signal(SIGTTOU, saved_sigttou_callback);455}456return true;457}458#endif // LLDB_ENABLE_POSIX459return false;460}461462bool TerminalState::IsValid() const {463return m_tty.FileDescriptorIsValid() &&464(TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());465}466467bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }468469bool TerminalState::TTYStateIsValid() const { return bool(m_data); }470471bool TerminalState::ProcessGroupIsValid() const {472return static_cast<int32_t>(m_process_group) != -1;473}474475476