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