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