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-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 <csignal> 16 #include <fcntl.h> 17 18 #if LLDB_ENABLE_TERMIOS 19 #include <termios.h> 20 #endif 21 22 using namespace lldb_private; 23 24 bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); } 25 26 bool 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 55 bool 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 struct TerminalState::Data { 85 #if LLDB_ENABLE_TERMIOS 86 struct termios m_termios; ///< Cached terminal state information. 87 #endif 88 }; 89 90 TerminalState::TerminalState(Terminal term, bool save_process_group) 91 : m_tty(term) { 92 Save(term, save_process_group); 93 } 94 95 TerminalState::~TerminalState() { Restore(); } 96 97 void TerminalState::Clear() { 98 m_tty.Clear(); 99 m_tflags = -1; 100 m_data.reset(); 101 m_process_group = -1; 102 } 103 104 bool TerminalState::Save(Terminal term, bool save_process_group) { 105 Clear(); 106 m_tty = term; 107 if (m_tty.IsATerminal()) { 108 int fd = m_tty.GetFileDescriptor(); 109 #if LLDB_ENABLE_POSIX 110 m_tflags = ::fcntl(fd, F_GETFL, 0); 111 #if LLDB_ENABLE_TERMIOS 112 std::unique_ptr<Data> new_data{new Data()}; 113 if (::tcgetattr(fd, &new_data->m_termios) == 0) 114 m_data = std::move(new_data); 115 #endif // LLDB_ENABLE_TERMIOS 116 if (save_process_group) 117 m_process_group = ::tcgetpgrp(fd); 118 #endif // LLDB_ENABLE_POSIX 119 } 120 return IsValid(); 121 } 122 123 bool TerminalState::Restore() const { 124 #if LLDB_ENABLE_POSIX 125 if (IsValid()) { 126 const int fd = m_tty.GetFileDescriptor(); 127 if (TFlagsIsValid()) 128 fcntl(fd, F_SETFL, m_tflags); 129 130 #if LLDB_ENABLE_TERMIOS 131 if (TTYStateIsValid()) 132 tcsetattr(fd, TCSANOW, &m_data->m_termios); 133 #endif // LLDB_ENABLE_TERMIOS 134 135 if (ProcessGroupIsValid()) { 136 // Save the original signal handler. 137 void (*saved_sigttou_callback)(int) = nullptr; 138 saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN); 139 // Set the process group 140 tcsetpgrp(fd, m_process_group); 141 // Restore the original signal handler. 142 signal(SIGTTOU, saved_sigttou_callback); 143 } 144 return true; 145 } 146 #endif // LLDB_ENABLE_POSIX 147 return false; 148 } 149 150 bool TerminalState::IsValid() const { 151 return m_tty.FileDescriptorIsValid() && 152 (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid()); 153 } 154 155 bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; } 156 157 bool TerminalState::TTYStateIsValid() const { return bool(m_data); } 158 159 bool TerminalState::ProcessGroupIsValid() const { 160 return static_cast<int32_t>(m_process_group) != -1; 161 } 162