Terminal.cpp revision 360784
1//===-- Terminal.cpp --------------------------------------------*- C++ -*-===// 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-exception 6// 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 <fcntl.h> 16#include <signal.h> 17 18#if LLDB_ENABLE_TERMIOS 19#include <termios.h> 20#endif 21 22using namespace lldb_private; 23 24bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); } 25 26bool Terminal::SetEcho(bool enabled) { 27 if (FileDescriptorIsValid()) { 28#if LLDB_ENABLE_TERMIOS 29 if (IsATerminal()) { 30 struct termios fd_termios; 31 if (::tcgetattr(m_fd, &fd_termios) == 0) { 32 bool set_corectly = false; 33 if (enabled) { 34 if (fd_termios.c_lflag & ECHO) 35 set_corectly = true; 36 else 37 fd_termios.c_lflag |= ECHO; 38 } else { 39 if (fd_termios.c_lflag & ECHO) 40 fd_termios.c_lflag &= ~ECHO; 41 else 42 set_corectly = true; 43 } 44 45 if (set_corectly) 46 return true; 47 return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0; 48 } 49 } 50#endif // #if LLDB_ENABLE_TERMIOS 51 } 52 return false; 53} 54 55bool Terminal::SetCanonical(bool enabled) { 56 if (FileDescriptorIsValid()) { 57#if LLDB_ENABLE_TERMIOS 58 if (IsATerminal()) { 59 struct termios fd_termios; 60 if (::tcgetattr(m_fd, &fd_termios) == 0) { 61 bool set_corectly = false; 62 if (enabled) { 63 if (fd_termios.c_lflag & ICANON) 64 set_corectly = true; 65 else 66 fd_termios.c_lflag |= ICANON; 67 } else { 68 if (fd_termios.c_lflag & ICANON) 69 fd_termios.c_lflag &= ~ICANON; 70 else 71 set_corectly = true; 72 } 73 74 if (set_corectly) 75 return true; 76 return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0; 77 } 78 } 79#endif // #if LLDB_ENABLE_TERMIOS 80 } 81 return false; 82} 83 84// Default constructor 85TerminalState::TerminalState() 86 : m_tty(), m_tflags(-1), 87#if LLDB_ENABLE_TERMIOS 88 m_termios_up(), 89#endif 90 m_process_group(-1) { 91} 92 93// Destructor 94TerminalState::~TerminalState() {} 95 96void TerminalState::Clear() { 97 m_tty.Clear(); 98 m_tflags = -1; 99#if LLDB_ENABLE_TERMIOS 100 m_termios_up.reset(); 101#endif 102 m_process_group = -1; 103} 104 105// Save the current state of the TTY for the file descriptor "fd" and if 106// "save_process_group" is true, attempt to save the process group info for the 107// TTY. 108bool TerminalState::Save(int fd, bool save_process_group) { 109 m_tty.SetFileDescriptor(fd); 110 if (m_tty.IsATerminal()) { 111#if LLDB_ENABLE_POSIX 112 m_tflags = ::fcntl(fd, F_GETFL, 0); 113#endif 114#if LLDB_ENABLE_TERMIOS 115 if (m_termios_up == nullptr) 116 m_termios_up.reset(new struct termios); 117 int err = ::tcgetattr(fd, m_termios_up.get()); 118 if (err != 0) 119 m_termios_up.reset(); 120#endif // #if LLDB_ENABLE_TERMIOS 121#if LLDB_ENABLE_POSIX 122 if (save_process_group) 123 m_process_group = ::tcgetpgrp(0); 124 else 125 m_process_group = -1; 126#endif 127 } else { 128 m_tty.Clear(); 129 m_tflags = -1; 130#if LLDB_ENABLE_TERMIOS 131 m_termios_up.reset(); 132#endif 133 m_process_group = -1; 134 } 135 return IsValid(); 136} 137 138// Restore the state of the TTY using the cached values from a previous call to 139// Save(). 140bool TerminalState::Restore() const { 141#if LLDB_ENABLE_POSIX 142 if (IsValid()) { 143 const int fd = m_tty.GetFileDescriptor(); 144 if (TFlagsIsValid()) 145 fcntl(fd, F_SETFL, m_tflags); 146 147#if LLDB_ENABLE_TERMIOS 148 if (TTYStateIsValid()) 149 tcsetattr(fd, TCSANOW, m_termios_up.get()); 150#endif // #if LLDB_ENABLE_TERMIOS 151 152 if (ProcessGroupIsValid()) { 153 // Save the original signal handler. 154 void (*saved_sigttou_callback)(int) = nullptr; 155 saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN); 156 // Set the process group 157 tcsetpgrp(fd, m_process_group); 158 // Restore the original signal handler. 159 signal(SIGTTOU, saved_sigttou_callback); 160 } 161 return true; 162 } 163#endif 164 return false; 165} 166 167// Returns true if this object has valid saved TTY state settings that can be 168// used to restore a previous state. 169bool TerminalState::IsValid() const { 170 return m_tty.FileDescriptorIsValid() && 171 (TFlagsIsValid() || TTYStateIsValid()); 172} 173 174// Returns true if m_tflags is valid 175bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; } 176 177// Returns true if m_ttystate is valid 178bool TerminalState::TTYStateIsValid() const { 179#if LLDB_ENABLE_TERMIOS 180 return m_termios_up != nullptr; 181#else 182 return false; 183#endif 184} 185 186// Returns true if m_process_group is valid 187bool TerminalState::ProcessGroupIsValid() const { 188 return static_cast<int32_t>(m_process_group) != -1; 189} 190 191// Constructor 192TerminalStateSwitcher::TerminalStateSwitcher() : m_currentState(UINT32_MAX) {} 193 194// Destructor 195TerminalStateSwitcher::~TerminalStateSwitcher() {} 196 197// Returns the number of states that this switcher contains 198uint32_t TerminalStateSwitcher::GetNumberOfStates() const { 199 return llvm::array_lengthof(m_ttystates); 200} 201 202// Restore the state at index "idx". 203// 204// Returns true if the restore was successful, false otherwise. 205bool TerminalStateSwitcher::Restore(uint32_t idx) const { 206 const uint32_t num_states = GetNumberOfStates(); 207 if (idx >= num_states) 208 return false; 209 210 // See if we already are in this state? 211 if (m_currentState < num_states && (idx == m_currentState) && 212 m_ttystates[idx].IsValid()) 213 return true; 214 215 // Set the state to match the index passed in and only update the current 216 // state if there are no errors. 217 if (m_ttystates[idx].Restore()) { 218 m_currentState = idx; 219 return true; 220 } 221 222 // We failed to set the state. The tty state was invalid or not initialized. 223 return false; 224} 225 226// Save the state at index "idx" for file descriptor "fd" and save the process 227// group if requested. 228// 229// Returns true if the restore was successful, false otherwise. 230bool TerminalStateSwitcher::Save(uint32_t idx, int fd, 231 bool save_process_group) { 232 const uint32_t num_states = GetNumberOfStates(); 233 if (idx < num_states) 234 return m_ttystates[idx].Save(fd, save_process_group); 235 return false; 236} 237