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