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