Coverage Report

Created: 2023-09-12 09:32

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