1ac7ddfbfSEd Maste //===-- Terminal.cpp --------------------------------------------*- C++ -*-===//
2ac7ddfbfSEd Maste //
3ac7ddfbfSEd Maste // The LLVM Compiler Infrastructure
4ac7ddfbfSEd Maste //
5ac7ddfbfSEd Maste // This file is distributed under the University of Illinois Open Source
6ac7ddfbfSEd Maste // License. See LICENSE.TXT for details.
7ac7ddfbfSEd Maste //
8ac7ddfbfSEd Maste //===----------------------------------------------------------------------===//
9ac7ddfbfSEd Maste
10ac7ddfbfSEd Maste #include "lldb/Host/Terminal.h"
11435933ddSDimitry Andric
12*4ba319b5SDimitry Andric #include "lldb/Host/Config.h"
13435933ddSDimitry Andric #include "lldb/Host/PosixApi.h"
140127ef0fSEd Maste #include "llvm/ADT/STLExtras.h"
15ac7ddfbfSEd Maste
16ac7ddfbfSEd Maste #include <fcntl.h>
17ac7ddfbfSEd Maste #include <signal.h>
18ac7ddfbfSEd Maste
19ac7ddfbfSEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
20ac7ddfbfSEd Maste #include <termios.h>
21ac7ddfbfSEd Maste #endif
22ac7ddfbfSEd Maste
23ac7ddfbfSEd Maste using namespace lldb_private;
24ac7ddfbfSEd Maste
IsATerminal() const25435933ddSDimitry Andric bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
2635617911SEd Maste
SetEcho(bool enabled)27435933ddSDimitry Andric bool Terminal::SetEcho(bool enabled) {
28435933ddSDimitry Andric if (FileDescriptorIsValid()) {
29ac7ddfbfSEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
30435933ddSDimitry Andric if (IsATerminal()) {
31ac7ddfbfSEd Maste struct termios fd_termios;
32435933ddSDimitry Andric if (::tcgetattr(m_fd, &fd_termios) == 0) {
33ac7ddfbfSEd Maste bool set_corectly = false;
34435933ddSDimitry Andric if (enabled) {
35ac7ddfbfSEd Maste if (fd_termios.c_lflag & ECHO)
36ac7ddfbfSEd Maste set_corectly = true;
37ac7ddfbfSEd Maste else
38ac7ddfbfSEd Maste fd_termios.c_lflag |= ECHO;
39435933ddSDimitry Andric } else {
40ac7ddfbfSEd Maste if (fd_termios.c_lflag & ECHO)
41ac7ddfbfSEd Maste fd_termios.c_lflag &= ~ECHO;
42ac7ddfbfSEd Maste else
43ac7ddfbfSEd Maste set_corectly = true;
44ac7ddfbfSEd Maste }
45ac7ddfbfSEd Maste
46ac7ddfbfSEd Maste if (set_corectly)
47ac7ddfbfSEd Maste return true;
48ac7ddfbfSEd Maste return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
49ac7ddfbfSEd Maste }
50ac7ddfbfSEd Maste }
51ac7ddfbfSEd Maste #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
52ac7ddfbfSEd Maste }
53ac7ddfbfSEd Maste return false;
54ac7ddfbfSEd Maste }
55ac7ddfbfSEd Maste
SetCanonical(bool enabled)56435933ddSDimitry Andric bool Terminal::SetCanonical(bool enabled) {
57435933ddSDimitry Andric if (FileDescriptorIsValid()) {
58ac7ddfbfSEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
59435933ddSDimitry Andric if (IsATerminal()) {
60ac7ddfbfSEd Maste struct termios fd_termios;
61435933ddSDimitry Andric if (::tcgetattr(m_fd, &fd_termios) == 0) {
62ac7ddfbfSEd Maste bool set_corectly = false;
63435933ddSDimitry Andric if (enabled) {
64ac7ddfbfSEd Maste if (fd_termios.c_lflag & ICANON)
65ac7ddfbfSEd Maste set_corectly = true;
66ac7ddfbfSEd Maste else
67ac7ddfbfSEd Maste fd_termios.c_lflag |= ICANON;
68435933ddSDimitry Andric } else {
69ac7ddfbfSEd Maste if (fd_termios.c_lflag & ICANON)
70ac7ddfbfSEd Maste fd_termios.c_lflag &= ~ICANON;
71ac7ddfbfSEd Maste else
72ac7ddfbfSEd Maste set_corectly = true;
73ac7ddfbfSEd Maste }
74ac7ddfbfSEd Maste
75ac7ddfbfSEd Maste if (set_corectly)
76ac7ddfbfSEd Maste return true;
77ac7ddfbfSEd Maste return ::tcsetattr(m_fd, TCSANOW, &fd_termios) == 0;
78ac7ddfbfSEd Maste }
79ac7ddfbfSEd Maste }
80ac7ddfbfSEd Maste #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
81ac7ddfbfSEd Maste }
82ac7ddfbfSEd Maste return false;
83ac7ddfbfSEd Maste }
84ac7ddfbfSEd Maste
85ac7ddfbfSEd Maste //----------------------------------------------------------------------
86ac7ddfbfSEd Maste // Default constructor
87ac7ddfbfSEd Maste //----------------------------------------------------------------------
TerminalState()88435933ddSDimitry Andric TerminalState::TerminalState()
89435933ddSDimitry Andric : m_tty(), m_tflags(-1),
9035617911SEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
91ac7ddfbfSEd Maste m_termios_ap(),
9235617911SEd Maste #endif
93435933ddSDimitry Andric m_process_group(-1) {
94ac7ddfbfSEd Maste }
95ac7ddfbfSEd Maste
96ac7ddfbfSEd Maste //----------------------------------------------------------------------
97ac7ddfbfSEd Maste // Destructor
98ac7ddfbfSEd Maste //----------------------------------------------------------------------
~TerminalState()99435933ddSDimitry Andric TerminalState::~TerminalState() {}
100ac7ddfbfSEd Maste
Clear()101435933ddSDimitry Andric void TerminalState::Clear() {
102ac7ddfbfSEd Maste m_tty.Clear();
103ac7ddfbfSEd Maste m_tflags = -1;
10435617911SEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
105ac7ddfbfSEd Maste m_termios_ap.reset();
10635617911SEd Maste #endif
107ac7ddfbfSEd Maste m_process_group = -1;
108ac7ddfbfSEd Maste }
109ac7ddfbfSEd Maste
110ac7ddfbfSEd Maste //----------------------------------------------------------------------
111*4ba319b5SDimitry Andric // Save the current state of the TTY for the file descriptor "fd" and if
112*4ba319b5SDimitry Andric // "save_process_group" is true, attempt to save the process group info for the
113*4ba319b5SDimitry Andric // TTY.
114ac7ddfbfSEd Maste //----------------------------------------------------------------------
Save(int fd,bool save_process_group)115435933ddSDimitry Andric bool TerminalState::Save(int fd, bool save_process_group) {
116ac7ddfbfSEd Maste m_tty.SetFileDescriptor(fd);
117435933ddSDimitry Andric if (m_tty.IsATerminal()) {
11835617911SEd Maste #ifndef LLDB_DISABLE_POSIX
119ac7ddfbfSEd Maste m_tflags = ::fcntl(fd, F_GETFL, 0);
12035617911SEd Maste #endif
121ac7ddfbfSEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
122ac7ddfbfSEd Maste if (m_termios_ap.get() == NULL)
123ac7ddfbfSEd Maste m_termios_ap.reset(new struct termios);
124ac7ddfbfSEd Maste int err = ::tcgetattr(fd, m_termios_ap.get());
125ac7ddfbfSEd Maste if (err != 0)
126ac7ddfbfSEd Maste m_termios_ap.reset();
127ac7ddfbfSEd Maste #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
12835617911SEd Maste #ifndef LLDB_DISABLE_POSIX
129ac7ddfbfSEd Maste if (save_process_group)
130ac7ddfbfSEd Maste m_process_group = ::tcgetpgrp(0);
131ac7ddfbfSEd Maste else
132ac7ddfbfSEd Maste m_process_group = -1;
13335617911SEd Maste #endif
134435933ddSDimitry Andric } else {
135ac7ddfbfSEd Maste m_tty.Clear();
136ac7ddfbfSEd Maste m_tflags = -1;
13735617911SEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
138ac7ddfbfSEd Maste m_termios_ap.reset();
13935617911SEd Maste #endif
140ac7ddfbfSEd Maste m_process_group = -1;
141ac7ddfbfSEd Maste }
142ac7ddfbfSEd Maste return IsValid();
143ac7ddfbfSEd Maste }
144ac7ddfbfSEd Maste
145ac7ddfbfSEd Maste //----------------------------------------------------------------------
146*4ba319b5SDimitry Andric // Restore the state of the TTY using the cached values from a previous call to
147*4ba319b5SDimitry Andric // Save().
148ac7ddfbfSEd Maste //----------------------------------------------------------------------
Restore() const149435933ddSDimitry Andric bool TerminalState::Restore() const {
1501c3bbb01SEd Maste #ifndef LLDB_DISABLE_POSIX
151435933ddSDimitry Andric if (IsValid()) {
152ac7ddfbfSEd Maste const int fd = m_tty.GetFileDescriptor();
153ac7ddfbfSEd Maste if (TFlagsIsValid())
154ac7ddfbfSEd Maste fcntl(fd, F_SETFL, m_tflags);
155ac7ddfbfSEd Maste
156ac7ddfbfSEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
157ac7ddfbfSEd Maste if (TTYStateIsValid())
158ac7ddfbfSEd Maste tcsetattr(fd, TCSANOW, m_termios_ap.get());
159ac7ddfbfSEd Maste #endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
160ac7ddfbfSEd Maste
161435933ddSDimitry Andric if (ProcessGroupIsValid()) {
162ac7ddfbfSEd Maste // Save the original signal handler.
163ac7ddfbfSEd Maste void (*saved_sigttou_callback)(int) = NULL;
164ac7ddfbfSEd Maste saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
165ac7ddfbfSEd Maste // Set the process group
166ac7ddfbfSEd Maste tcsetpgrp(fd, m_process_group);
167ac7ddfbfSEd Maste // Restore the original signal handler.
168ac7ddfbfSEd Maste signal(SIGTTOU, saved_sigttou_callback);
169ac7ddfbfSEd Maste }
170ac7ddfbfSEd Maste return true;
171ac7ddfbfSEd Maste }
1721c3bbb01SEd Maste #endif
173ac7ddfbfSEd Maste return false;
174ac7ddfbfSEd Maste }
175ac7ddfbfSEd Maste
176ac7ddfbfSEd Maste //----------------------------------------------------------------------
177*4ba319b5SDimitry Andric // Returns true if this object has valid saved TTY state settings that can be
178*4ba319b5SDimitry Andric // used to restore a previous state.
179ac7ddfbfSEd Maste //----------------------------------------------------------------------
IsValid() const180435933ddSDimitry Andric bool TerminalState::IsValid() const {
181435933ddSDimitry Andric return m_tty.FileDescriptorIsValid() &&
182435933ddSDimitry Andric (TFlagsIsValid() || TTYStateIsValid());
183ac7ddfbfSEd Maste }
184ac7ddfbfSEd Maste
185ac7ddfbfSEd Maste //----------------------------------------------------------------------
186ac7ddfbfSEd Maste // Returns true if m_tflags is valid
187ac7ddfbfSEd Maste //----------------------------------------------------------------------
TFlagsIsValid() const188435933ddSDimitry Andric bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
189ac7ddfbfSEd Maste
190ac7ddfbfSEd Maste //----------------------------------------------------------------------
191ac7ddfbfSEd Maste // Returns true if m_ttystate is valid
192ac7ddfbfSEd Maste //----------------------------------------------------------------------
TTYStateIsValid() const193435933ddSDimitry Andric bool TerminalState::TTYStateIsValid() const {
19435617911SEd Maste #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED
195ac7ddfbfSEd Maste return m_termios_ap.get() != 0;
19635617911SEd Maste #else
19735617911SEd Maste return false;
19835617911SEd Maste #endif
199ac7ddfbfSEd Maste }
200ac7ddfbfSEd Maste
201ac7ddfbfSEd Maste //----------------------------------------------------------------------
202ac7ddfbfSEd Maste // Returns true if m_process_group is valid
203ac7ddfbfSEd Maste //----------------------------------------------------------------------
ProcessGroupIsValid() const204435933ddSDimitry Andric bool TerminalState::ProcessGroupIsValid() const {
2050127ef0fSEd Maste return static_cast<int32_t>(m_process_group) != -1;
206ac7ddfbfSEd Maste }
207ac7ddfbfSEd Maste
208ac7ddfbfSEd Maste //------------------------------------------------------------------
209ac7ddfbfSEd Maste // Constructor
210ac7ddfbfSEd Maste //------------------------------------------------------------------
TerminalStateSwitcher()211435933ddSDimitry Andric TerminalStateSwitcher::TerminalStateSwitcher() : m_currentState(UINT32_MAX) {}
212ac7ddfbfSEd Maste
213ac7ddfbfSEd Maste //------------------------------------------------------------------
214ac7ddfbfSEd Maste // Destructor
215ac7ddfbfSEd Maste //------------------------------------------------------------------
~TerminalStateSwitcher()216435933ddSDimitry Andric TerminalStateSwitcher::~TerminalStateSwitcher() {}
217ac7ddfbfSEd Maste
218ac7ddfbfSEd Maste //------------------------------------------------------------------
219ac7ddfbfSEd Maste // Returns the number of states that this switcher contains
220ac7ddfbfSEd Maste //------------------------------------------------------------------
GetNumberOfStates() const221435933ddSDimitry Andric uint32_t TerminalStateSwitcher::GetNumberOfStates() const {
2220127ef0fSEd Maste return llvm::array_lengthof(m_ttystates);
223ac7ddfbfSEd Maste }
224ac7ddfbfSEd Maste
225ac7ddfbfSEd Maste //------------------------------------------------------------------
226ac7ddfbfSEd Maste // Restore the state at index "idx".
227ac7ddfbfSEd Maste //
228ac7ddfbfSEd Maste // Returns true if the restore was successful, false otherwise.
229ac7ddfbfSEd Maste //------------------------------------------------------------------
Restore(uint32_t idx) const230435933ddSDimitry Andric bool TerminalStateSwitcher::Restore(uint32_t idx) const {
231ac7ddfbfSEd Maste const uint32_t num_states = GetNumberOfStates();
232ac7ddfbfSEd Maste if (idx >= num_states)
233ac7ddfbfSEd Maste return false;
234ac7ddfbfSEd Maste
235ac7ddfbfSEd Maste // See if we already are in this state?
236435933ddSDimitry Andric if (m_currentState < num_states && (idx == m_currentState) &&
237435933ddSDimitry Andric m_ttystates[idx].IsValid())
238ac7ddfbfSEd Maste return true;
239ac7ddfbfSEd Maste
240*4ba319b5SDimitry Andric // Set the state to match the index passed in and only update the current
241*4ba319b5SDimitry Andric // state if there are no errors.
242435933ddSDimitry Andric if (m_ttystates[idx].Restore()) {
243ac7ddfbfSEd Maste m_currentState = idx;
244ac7ddfbfSEd Maste return true;
245ac7ddfbfSEd Maste }
246ac7ddfbfSEd Maste
247*4ba319b5SDimitry Andric // We failed to set the state. The tty state was invalid or not initialized.
248ac7ddfbfSEd Maste return false;
249ac7ddfbfSEd Maste }
250ac7ddfbfSEd Maste
251ac7ddfbfSEd Maste //------------------------------------------------------------------
252*4ba319b5SDimitry Andric // Save the state at index "idx" for file descriptor "fd" and save the process
253*4ba319b5SDimitry Andric // group if requested.
254ac7ddfbfSEd Maste //
255ac7ddfbfSEd Maste // Returns true if the restore was successful, false otherwise.
256ac7ddfbfSEd Maste //------------------------------------------------------------------
Save(uint32_t idx,int fd,bool save_process_group)257435933ddSDimitry Andric bool TerminalStateSwitcher::Save(uint32_t idx, int fd,
258435933ddSDimitry Andric bool save_process_group) {
259ac7ddfbfSEd Maste const uint32_t num_states = GetNumberOfStates();
260ac7ddfbfSEd Maste if (idx < num_states)
261ac7ddfbfSEd Maste return m_ttystates[idx].Save(fd, save_process_group);
262ac7ddfbfSEd Maste return false;
263ac7ddfbfSEd Maste }
264