1 //===-- Terminal.cpp ------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/Terminal.h"
10 
11 #include "lldb/Host/Config.h"
12 #include "lldb/Host/PosixApi.h"
13 #include "llvm/ADT/STLExtras.h"
14 
15 #include <csignal>
16 #include <fcntl.h>
17 
18 #if LLDB_ENABLE_TERMIOS
19 #include <termios.h>
20 #endif
21 
22 using namespace lldb_private;
23 
24 struct Terminal::Data {
25 #if LLDB_ENABLE_TERMIOS
26   struct termios m_termios; ///< Cached terminal state information.
27 #endif
28 };
29 
30 bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
31 
32 llvm::Expected<Terminal::Data> Terminal::GetData() {
33   if (!FileDescriptorIsValid())
34     return llvm::createStringError(llvm::inconvertibleErrorCode(),
35                                    "invalid fd");
36 
37 #if LLDB_ENABLE_TERMIOS
38   if (!IsATerminal())
39     return llvm::createStringError(llvm::inconvertibleErrorCode(),
40                                    "fd not a terminal");
41 
42   Data data;
43   if (::tcgetattr(m_fd, &data.m_termios) != 0)
44     return llvm::createStringError(
45         std::error_code(errno, std::generic_category()),
46         "unable to get teletype attributes");
47   return data;
48 #else // !LLDB_ENABLE_TERMIOS
49   return llvm::createStringError(llvm::inconvertibleErrorCode(),
50                                  "termios support missing in LLDB");
51 #endif // LLDB_ENABLE_TERMIOS
52 }
53 
54 llvm::Error Terminal::SetData(const Terminal::Data &data) {
55 #if LLDB_ENABLE_TERMIOS
56   assert(FileDescriptorIsValid());
57   assert(IsATerminal());
58 
59   if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)
60     return llvm::createStringError(
61         std::error_code(errno, std::generic_category()),
62         "unable to set teletype attributes");
63   return llvm::Error::success();
64 #else // !LLDB_ENABLE_TERMIOS
65   llvm_unreachable("SetData() should not be called if !LLDB_ENABLE_TERMIOS");
66 #endif // LLDB_ENABLE_TERMIOS
67 }
68 
69 llvm::Error Terminal::SetEcho(bool enabled) {
70   llvm::Expected<Data> data = GetData();
71   if (!data)
72     return data.takeError();
73 
74 #if LLDB_ENABLE_TERMIOS
75   struct termios &fd_termios = data->m_termios;
76   fd_termios.c_lflag &= ~ECHO;
77   if (enabled)
78     fd_termios.c_lflag |= ECHO;
79   return SetData(data.get());
80 #endif // LLDB_ENABLE_TERMIOS
81 }
82 
83 llvm::Error Terminal::SetCanonical(bool enabled) {
84   llvm::Expected<Data> data = GetData();
85   if (!data)
86     return data.takeError();
87 
88 #if LLDB_ENABLE_TERMIOS
89   struct termios &fd_termios = data->m_termios;
90   fd_termios.c_lflag &= ~ICANON;
91   if (enabled)
92     fd_termios.c_lflag |= ICANON;
93   return SetData(data.get());
94 #endif // LLDB_ENABLE_TERMIOS
95 }
96 
97 llvm::Error Terminal::SetRaw() {
98   llvm::Expected<Data> data = GetData();
99   if (!data)
100     return data.takeError();
101 
102 #if LLDB_ENABLE_TERMIOS
103   struct termios &fd_termios = data->m_termios;
104   ::cfmakeraw(&fd_termios);
105 
106   // Make sure only one character is needed to return from a read
107   // (cfmakeraw() doesn't do this on NetBSD)
108   fd_termios.c_cc[VMIN] = 1;
109   fd_termios.c_cc[VTIME] = 0;
110 
111   return SetData(data.get());
112 #endif // #if LLDB_ENABLE_TERMIOS
113 }
114 
115 #if LLDB_ENABLE_TERMIOS
116 static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
117   switch (baud_rate) {
118 #if defined(B50)
119   case 50:
120     return B50;
121 #endif
122 #if defined(B75)
123   case 75:
124     return B75;
125 #endif
126 #if defined(B110)
127   case 110:
128     return B110;
129 #endif
130 #if defined(B134)
131   case 134:
132     return B134;
133 #endif
134 #if defined(B150)
135   case 150:
136     return B150;
137 #endif
138 #if defined(B200)
139   case 200:
140     return B200;
141 #endif
142 #if defined(B300)
143   case 300:
144     return B300;
145 #endif
146 #if defined(B600)
147   case 600:
148     return B600;
149 #endif
150 #if defined(B1200)
151   case 1200:
152     return B1200;
153 #endif
154 #if defined(B1800)
155   case 1800:
156     return B1800;
157 #endif
158 #if defined(B2400)
159   case 2400:
160     return B2400;
161 #endif
162 #if defined(B4800)
163   case 4800:
164     return B4800;
165 #endif
166 #if defined(B9600)
167   case 9600:
168     return B9600;
169 #endif
170 #if defined(B19200)
171   case 19200:
172     return B19200;
173 #endif
174 #if defined(B38400)
175   case 38400:
176     return B38400;
177 #endif
178 #if defined(B57600)
179   case 57600:
180     return B57600;
181 #endif
182 #if defined(B115200)
183   case 115200:
184     return B115200;
185 #endif
186 #if defined(B230400)
187   case 230400:
188     return B230400;
189 #endif
190 #if defined(B460800)
191   case 460800:
192     return B460800;
193 #endif
194 #if defined(B500000)
195   case 500000:
196     return B500000;
197 #endif
198 #if defined(B576000)
199   case 576000:
200     return B576000;
201 #endif
202 #if defined(B921600)
203   case 921600:
204     return B921600;
205 #endif
206 #if defined(B1000000)
207   case 1000000:
208     return B1000000;
209 #endif
210 #if defined(B1152000)
211   case 1152000:
212     return B1152000;
213 #endif
214 #if defined(B1500000)
215   case 1500000:
216     return B1500000;
217 #endif
218 #if defined(B2000000)
219   case 2000000:
220     return B2000000;
221 #endif
222 #if defined(B76800)
223   case 76800:
224     return B76800;
225 #endif
226 #if defined(B153600)
227   case 153600:
228     return B153600;
229 #endif
230 #if defined(B307200)
231   case 307200:
232     return B307200;
233 #endif
234 #if defined(B614400)
235   case 614400:
236     return B614400;
237 #endif
238 #if defined(B2500000)
239   case 2500000:
240     return B2500000;
241 #endif
242 #if defined(B3000000)
243   case 3000000:
244     return B3000000;
245 #endif
246 #if defined(B3500000)
247   case 3500000:
248     return B3500000;
249 #endif
250 #if defined(B4000000)
251   case 4000000:
252     return B4000000;
253 #endif
254   default:
255     return llvm::None;
256   }
257 }
258 #endif
259 
260 llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
261   llvm::Expected<Data> data = GetData();
262   if (!data)
263     return data.takeError();
264 
265 #if LLDB_ENABLE_TERMIOS
266   struct termios &fd_termios = data->m_termios;
267   llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
268   if (!val) // invalid value
269     return llvm::createStringError(llvm::inconvertibleErrorCode(),
270                                    "baud rate %d unsupported by the platform",
271                                    baud_rate);
272   if (::cfsetispeed(&fd_termios, val.getValue()) != 0)
273     return llvm::createStringError(
274         std::error_code(errno, std::generic_category()),
275         "setting input baud rate failed");
276   if (::cfsetospeed(&fd_termios, val.getValue()) != 0)
277     return llvm::createStringError(
278         std::error_code(errno, std::generic_category()),
279         "setting output baud rate failed");
280   return SetData(data.get());
281 #endif // #if LLDB_ENABLE_TERMIOS
282 }
283 
284 llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
285   llvm::Expected<Data> data = GetData();
286   if (!data)
287     return data.takeError();
288 
289 #if LLDB_ENABLE_TERMIOS
290   struct termios &fd_termios = data->m_termios;
291   switch (stop_bits) {
292   case 1:
293     fd_termios.c_cflag &= ~CSTOPB;
294     break;
295   case 2:
296     fd_termios.c_cflag |= CSTOPB;
297     break;
298   default:
299     return llvm::createStringError(
300         llvm::inconvertibleErrorCode(),
301         "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
302   }
303   return SetData(data.get());
304 #endif // #if LLDB_ENABLE_TERMIOS
305 }
306 
307 llvm::Error Terminal::SetParity(Terminal::Parity parity) {
308   llvm::Expected<Data> data = GetData();
309   if (!data)
310     return data.takeError();
311 
312 #if LLDB_ENABLE_TERMIOS
313   struct termios &fd_termios = data->m_termios;
314   fd_termios.c_cflag &= ~(
315 #if defined(CMSPAR)
316       CMSPAR |
317 #endif
318       PARENB | PARODD);
319 
320   if (parity != Parity::No) {
321     fd_termios.c_cflag |= PARENB;
322     if (parity == Parity::Odd || parity == Parity::Mark)
323       fd_termios.c_cflag |= PARODD;
324     if (parity == Parity::Mark || parity == Parity::Space) {
325 #if defined(CMSPAR)
326       fd_termios.c_cflag |= CMSPAR;
327 #else
328       return llvm::createStringError(
329           llvm::inconvertibleErrorCode(),
330           "space/mark parity is not supported by the platform");
331 #endif
332     }
333   }
334   return SetData(data.get());
335 #endif // #if LLDB_ENABLE_TERMIOS
336 }
337 
338 llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {
339   llvm::Expected<Data> data = GetData();
340   if (!data)
341     return data.takeError();
342 
343 #if LLDB_ENABLE_TERMIOS
344   struct termios &fd_termios = data->m_termios;
345   fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
346 
347   if (parity_check != ParityCheck::No) {
348     fd_termios.c_iflag |= INPCK;
349     if (parity_check == ParityCheck::Ignore)
350       fd_termios.c_iflag |= IGNPAR;
351     else if (parity_check == ParityCheck::Mark)
352       fd_termios.c_iflag |= PARMRK;
353   }
354   return SetData(data.get());
355 #endif // #if LLDB_ENABLE_TERMIOS
356 }
357 
358 llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
359   llvm::Expected<Data> data = GetData();
360   if (!data)
361     return data.takeError();
362 
363 #if LLDB_ENABLE_TERMIOS
364 #if defined(CRTSCTS)
365   struct termios &fd_termios = data->m_termios;
366   fd_termios.c_cflag &= ~CRTSCTS;
367   if (enabled)
368     fd_termios.c_cflag |= CRTSCTS;
369   return SetData(data.get());
370 #else  // !defined(CRTSCTS)
371   if (enabled)
372     return llvm::createStringError(
373         llvm::inconvertibleErrorCode(),
374         "hardware flow control is not supported by the platform");
375   return llvm::Error::success();
376 #endif // defined(CRTSCTS)
377 #endif // #if LLDB_ENABLE_TERMIOS
378 }
379 
380 TerminalState::TerminalState(Terminal term, bool save_process_group)
381     : m_tty(term) {
382   Save(term, save_process_group);
383 }
384 
385 TerminalState::~TerminalState() { Restore(); }
386 
387 void TerminalState::Clear() {
388   m_tty.Clear();
389   m_tflags = -1;
390   m_data.reset();
391   m_process_group = -1;
392 }
393 
394 bool TerminalState::Save(Terminal term, bool save_process_group) {
395   Clear();
396   m_tty = term;
397   if (m_tty.IsATerminal()) {
398     int fd = m_tty.GetFileDescriptor();
399 #if LLDB_ENABLE_POSIX
400     m_tflags = ::fcntl(fd, F_GETFL, 0);
401 #if LLDB_ENABLE_TERMIOS
402     std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};
403     if (::tcgetattr(fd, &new_data->m_termios) == 0)
404       m_data = std::move(new_data);
405 #endif // LLDB_ENABLE_TERMIOS
406     if (save_process_group)
407       m_process_group = ::tcgetpgrp(fd);
408 #endif // LLDB_ENABLE_POSIX
409   }
410   return IsValid();
411 }
412 
413 bool TerminalState::Restore() const {
414 #if LLDB_ENABLE_POSIX
415   if (IsValid()) {
416     const int fd = m_tty.GetFileDescriptor();
417     if (TFlagsIsValid())
418       fcntl(fd, F_SETFL, m_tflags);
419 
420 #if LLDB_ENABLE_TERMIOS
421     if (TTYStateIsValid())
422       tcsetattr(fd, TCSANOW, &m_data->m_termios);
423 #endif // LLDB_ENABLE_TERMIOS
424 
425     if (ProcessGroupIsValid()) {
426       // Save the original signal handler.
427       void (*saved_sigttou_callback)(int) = nullptr;
428       saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
429       // Set the process group
430       tcsetpgrp(fd, m_process_group);
431       // Restore the original signal handler.
432       signal(SIGTTOU, saved_sigttou_callback);
433     }
434     return true;
435   }
436 #endif // LLDB_ENABLE_POSIX
437   return false;
438 }
439 
440 bool TerminalState::IsValid() const {
441   return m_tty.FileDescriptorIsValid() &&
442          (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());
443 }
444 
445 bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
446 
447 bool TerminalState::TTYStateIsValid() const { return bool(m_data); }
448 
449 bool TerminalState::ProcessGroupIsValid() const {
450   return static_cast<int32_t>(m_process_group) != -1;
451 }
452