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 "lldb/Host/Config.h" 12 13 #include <fcntl.h> 14 #include <unistd.h> 15 #include <signal.h> 16 17 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED 18 #include <termios.h> 19 #endif 20 21 22 using namespace lldb_private; 23 24 bool 25 Terminal::IsATerminal () const 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 m_termios_ap(), 112 m_process_group(-1) 113 { 114 } 115 116 //---------------------------------------------------------------------- 117 // Destructor 118 //---------------------------------------------------------------------- 119 TerminalState::~TerminalState() 120 { 121 } 122 123 //---------------------------------------------------------------------- 124 // Save the current state of the TTY for the file descriptor "fd" 125 // and if "save_process_group" is true, attempt to save the process 126 // group info for the TTY. 127 //---------------------------------------------------------------------- 128 bool 129 TerminalState::Save (int fd, bool save_process_group) 130 { 131 m_tty.SetFileDescriptor(fd); 132 if (m_tty.IsATerminal()) 133 { 134 m_tflags = ::fcntl (fd, F_GETFL, 0); 135 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED 136 if (m_termios_ap.get() == NULL) 137 m_termios_ap.reset (new struct termios); 138 int err = ::tcgetattr (fd, m_termios_ap.get()); 139 if (err != 0) 140 m_termios_ap.reset(); 141 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED 142 if (save_process_group) 143 m_process_group = ::tcgetpgrp (0); 144 else 145 m_process_group = -1; 146 } 147 else 148 { 149 m_tty.Clear(); 150 m_tflags = -1; 151 m_termios_ap.reset(); 152 m_process_group = -1; 153 } 154 return IsValid(); 155 } 156 157 //---------------------------------------------------------------------- 158 // Restore the state of the TTY using the cached values from a 159 // previous call to Save(). 160 //---------------------------------------------------------------------- 161 bool 162 TerminalState::Restore () const 163 { 164 int result = 0; 165 if (IsValid()) 166 { 167 const int fd = m_tty.GetFileDescriptor(); 168 if (TFlagsIsValid()) 169 result = fcntl (fd, F_SETFL, m_tflags); 170 171 #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED 172 if (TTYStateIsValid()) 173 result = tcsetattr (fd, TCSANOW, m_termios_ap.get()); 174 #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED 175 176 if (ProcessGroupIsValid()) 177 { 178 // Save the original signal handler. 179 void (*saved_sigttou_callback) (int) = NULL; 180 saved_sigttou_callback = (void (*)(int)) signal (SIGTTOU, SIG_IGN); 181 // Set the process group 182 result = tcsetpgrp (fd, m_process_group); 183 // Restore the original signal handler. 184 signal (SIGTTOU, saved_sigttou_callback); 185 } 186 return true; 187 } 188 return false; 189 } 190 191 192 193 194 //---------------------------------------------------------------------- 195 // Returns true if this object has valid saved TTY state settings 196 // that can be used to restore a previous state. 197 //---------------------------------------------------------------------- 198 bool 199 TerminalState::IsValid() const 200 { 201 return m_tty.FileDescriptorIsValid () && (TFlagsIsValid() || TTYStateIsValid()); 202 } 203 204 //---------------------------------------------------------------------- 205 // Returns true if m_tflags is valid 206 //---------------------------------------------------------------------- 207 bool 208 TerminalState::TFlagsIsValid() const 209 { 210 return m_tflags != -1; 211 } 212 213 //---------------------------------------------------------------------- 214 // Returns true if m_ttystate is valid 215 //---------------------------------------------------------------------- 216 bool 217 TerminalState::TTYStateIsValid() const 218 { 219 return m_termios_ap.get() != 0; 220 } 221 222 //---------------------------------------------------------------------- 223 // Returns true if m_process_group is valid 224 //---------------------------------------------------------------------- 225 bool 226 TerminalState::ProcessGroupIsValid() const 227 { 228 return m_process_group != -1; 229 } 230 231 //------------------------------------------------------------------ 232 // Constructor 233 //------------------------------------------------------------------ 234 TerminalStateSwitcher::TerminalStateSwitcher () : 235 m_currentState(UINT32_MAX) 236 { 237 } 238 239 //------------------------------------------------------------------ 240 // Destructor 241 //------------------------------------------------------------------ 242 TerminalStateSwitcher::~TerminalStateSwitcher () 243 { 244 } 245 246 //------------------------------------------------------------------ 247 // Returns the number of states that this switcher contains 248 //------------------------------------------------------------------ 249 uint32_t 250 TerminalStateSwitcher::GetNumberOfStates() const 251 { 252 return sizeof(m_ttystates)/sizeof(TerminalState); 253 } 254 255 //------------------------------------------------------------------ 256 // Restore the state at index "idx". 257 // 258 // Returns true if the restore was successful, false otherwise. 259 //------------------------------------------------------------------ 260 bool 261 TerminalStateSwitcher::Restore (uint32_t idx) const 262 { 263 const uint32_t num_states = GetNumberOfStates(); 264 if (idx >= num_states) 265 return false; 266 267 // See if we already are in this state? 268 if (m_currentState < num_states && (idx == m_currentState) && m_ttystates[idx].IsValid()) 269 return true; 270 271 // Set the state to match the index passed in and only update the 272 // current state if there are no errors. 273 if (m_ttystates[idx].Restore()) 274 { 275 m_currentState = idx; 276 return true; 277 } 278 279 // We failed to set the state. The tty state was invalid or not 280 // initialized. 281 return false; 282 } 283 284 //------------------------------------------------------------------ 285 // Save the state at index "idx" for file descriptor "fd" and 286 // save the process group if requested. 287 // 288 // Returns true if the restore was successful, false otherwise. 289 //------------------------------------------------------------------ 290 bool 291 TerminalStateSwitcher::Save (uint32_t idx, int fd, bool save_process_group) 292 { 293 const uint32_t num_states = GetNumberOfStates(); 294 if (idx < num_states) 295 return m_ttystates[idx].Save(fd, save_process_group); 296 return false; 297 } 298 299 300