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