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