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