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