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