Coverage Report

Created: 2022-01-18 06:27

/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
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
6.84k
bool Terminal::IsATerminal() const { return m_fd >= 0 && 
::isatty(m_fd)896
; }
31
32
#if !LLDB_ENABLE_TERMIOS
33
static llvm::Error termiosMissingError() {
34
  return llvm::createStringError(llvm::inconvertibleErrorCode(),
35
                                 "termios support missing in LLDB");
36
}
37
#endif
38
39
38
llvm::Expected<Terminal::Data> Terminal::GetData() {
40
38
#if LLDB_ENABLE_TERMIOS
41
38
  if (!FileDescriptorIsValid())
42
0
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
43
0
                                   "invalid fd");
44
45
38
  if (!IsATerminal())
46
0
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
47
0
                                   "fd not a terminal");
48
49
38
  Data data;
50
38
  if (::tcgetattr(m_fd, &data.m_termios) != 0)
51
0
    return llvm::createStringError(
52
0
        std::error_code(errno, std::generic_category()),
53
0
        "unable to get teletype attributes");
54
38
  return data;
55
#else // !LLDB_ENABLE_TERMIOS
56
  return termiosMissingError();
57
#endif // LLDB_ENABLE_TERMIOS
58
38
}
59
60
33
llvm::Error Terminal::SetData(const Terminal::Data &data) {
61
33
#if LLDB_ENABLE_TERMIOS
62
33
  assert(FileDescriptorIsValid());
63
0
  assert(IsATerminal());
64
65
33
  if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)
66
0
    return llvm::createStringError(
67
0
        std::error_code(errno, std::generic_category()),
68
0
        "unable to set teletype attributes");
69
33
  return llvm::Error::success();
70
#else // !LLDB_ENABLE_TERMIOS
71
  return termiosMissingError();
72
#endif // LLDB_ENABLE_TERMIOS
73
33
}
74
75
7
llvm::Error Terminal::SetEcho(bool enabled) {
76
7
#if LLDB_ENABLE_TERMIOS
77
7
  llvm::Expected<Data> data = GetData();
78
7
  if (!data)
79
0
    return data.takeError();
80
81
7
  struct termios &fd_termios = data->m_termios;
82
7
  fd_termios.c_lflag &= ~ECHO;
83
7
  if (enabled)
84
1
    fd_termios.c_lflag |= ECHO;
85
7
  return SetData(data.get());
86
#else // !LLDB_ENABLE_TERMIOS
87
  return termiosMissingError();
88
#endif // LLDB_ENABLE_TERMIOS
89
7
}
90
91
7
llvm::Error Terminal::SetCanonical(bool enabled) {
92
7
#if LLDB_ENABLE_TERMIOS
93
7
  llvm::Expected<Data> data = GetData();
94
7
  if (!data)
95
0
    return data.takeError();
96
97
7
  struct termios &fd_termios = data->m_termios;
98
7
  fd_termios.c_lflag &= ~ICANON;
99
7
  if (enabled)
100
1
    fd_termios.c_lflag |= ICANON;
101
7
  return SetData(data.get());
102
#else // !LLDB_ENABLE_TERMIOS
103
  return termiosMissingError();
104
#endif // LLDB_ENABLE_TERMIOS
105
7
}
106
107
4
llvm::Error Terminal::SetRaw() {
108
4
#if LLDB_ENABLE_TERMIOS
109
4
  llvm::Expected<Data> data = GetData();
110
4
  if (!data)
111
0
    return data.takeError();
112
113
4
  struct termios &fd_termios = data->m_termios;
114
4
  ::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
4
  fd_termios.c_cc[VMIN] = 1;
119
4
  fd_termios.c_cc[VTIME] = 0;
120
121
4
  return SetData(data.get());
122
#else // !LLDB_ENABLE_TERMIOS
123
  return termiosMissingError();
124
#endif // LLDB_ENABLE_TERMIOS
125
4
}
126
127
#if LLDB_ENABLE_TERMIOS
128
4
static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
129
4
  switch (baud_rate) {
130
0
#if defined(B50)
131
0
  case 50:
132
0
    return B50;
133
0
#endif
134
0
#if defined(B75)
135
0
  case 75:
136
0
    return B75;
137
0
#endif
138
0
#if defined(B110)
139
0
  case 110:
140
0
    return B110;
141
0
#endif
142
0
#if defined(B134)
143
0
  case 134:
144
0
    return B134;
145
0
#endif
146
0
#if defined(B150)
147
0
  case 150:
148
0
    return B150;
149
0
#endif
150
0
#if defined(B200)
151
0
  case 200:
152
0
    return B200;
153
0
#endif
154
0
#if defined(B300)
155
0
  case 300:
156
0
    return B300;
157
0
#endif
158
0
#if defined(B600)
159
0
  case 600:
160
0
    return B600;
161
0
#endif
162
0
#if defined(B1200)
163
0
  case 1200:
164
0
    return B1200;
165
0
#endif
166
0
#if defined(B1800)
167
0
  case 1800:
168
0
    return B1800;
169
0
#endif
170
0
#if defined(B2400)
171
0
  case 2400:
172
0
    return B2400;
173
0
#endif
174
0
#if defined(B4800)
175
0
  case 4800:
176
0
    return B4800;
177
0
#endif
178
0
#if defined(B9600)
179
0
  case 9600:
180
0
    return B9600;
181
0
#endif
182
0
#if defined(B19200)
183
0
  case 19200:
184
0
    return B19200;
185
0
#endif
186
0
#if defined(B38400)
187
1
  case 38400:
188
1
    return B38400;
189
0
#endif
190
0
#if defined(B57600)
191
0
  case 57600:
192
0
    return B57600;
193
0
#endif
194
0
#if defined(B115200)
195
2
  case 115200:
196
2
    return B115200;
197
0
#endif
198
0
#if defined(B230400)
199
0
  case 230400:
200
0
    return B230400;
201
0
#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
0
#if defined(B76800)
235
0
  case 76800:
236
0
    return B76800;
237
0
#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
1
  default:
267
1
    return llvm::None;
268
4
  }
269
4
}
270
#endif
271
272
4
llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
273
4
#if LLDB_ENABLE_TERMIOS
274
4
  llvm::Expected<Data> data = GetData();
275
4
  if (!data)
276
0
    return data.takeError();
277
278
4
  struct termios &fd_termios = data->m_termios;
279
4
  llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
280
4
  if (!val) // invalid value
281
1
    return llvm::createStringError(llvm::inconvertibleErrorCode(),
282
1
                                   "baud rate %d unsupported by the platform",
283
1
                                   baud_rate);
284
3
  if (::cfsetispeed(&fd_termios, val.getValue()) != 0)
285
0
    return llvm::createStringError(
286
0
        std::error_code(errno, std::generic_category()),
287
0
        "setting input baud rate failed");
288
3
  if (::cfsetospeed(&fd_termios, val.getValue()) != 0)
289
0
    return llvm::createStringError(
290
0
        std::error_code(errno, std::generic_category()),
291
0
        "setting output baud rate failed");
292
3
  return SetData(data.get());
293
#else // !LLDB_ENABLE_TERMIOS
294
  return termiosMissingError();
295
#endif // LLDB_ENABLE_TERMIOS
296
3
}
297
298
5
llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
299
5
#if LLDB_ENABLE_TERMIOS
300
5
  llvm::Expected<Data> data = GetData();
301
5
  if (!data)
302
0
    return data.takeError();
303
304
5
  struct termios &fd_termios = data->m_termios;
305
5
  switch (stop_bits) {
306
1
  case 1:
307
1
    fd_termios.c_cflag &= ~CSTOPB;
308
1
    break;
309
2
  case 2:
310
2
    fd_termios.c_cflag |= CSTOPB;
311
2
    break;
312
2
  default:
313
2
    return llvm::createStringError(
314
2
        llvm::inconvertibleErrorCode(),
315
2
        "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
316
5
  }
317
3
  return SetData(data.get());
318
#else // !LLDB_ENABLE_TERMIOS
319
  return termiosMissingError();
320
#endif // LLDB_ENABLE_TERMIOS
321
5
}
322
323
5
llvm::Error Terminal::SetParity(Terminal::Parity parity) {
324
5
#if LLDB_ENABLE_TERMIOS
325
5
  llvm::Expected<Data> data = GetData();
326
5
  if (!data)
327
0
    return data.takeError();
328
329
5
  struct termios &fd_termios = data->m_termios;
330
5
  fd_termios.c_cflag &= ~(
331
#if defined(CMSPAR)
332
      CMSPAR |
333
#endif
334
5
      PARENB | PARODD);
335
336
5
  if (parity != Parity::No) {
337
4
    fd_termios.c_cflag |= PARENB;
338
4
    if (parity == Parity::Odd || 
parity == Parity::Mark3
)
339
2
      fd_termios.c_cflag |= PARODD;
340
4
    if (parity == Parity::Mark || 
parity == Parity::Space3
) {
341
#if defined(CMSPAR)
342
      fd_termios.c_cflag |= CMSPAR;
343
#else
344
2
      return llvm::createStringError(
345
2
          llvm::inconvertibleErrorCode(),
346
2
          "space/mark parity is not supported by the platform");
347
2
#endif
348
2
    }
349
4
  }
350
3
  return SetData(data.get());
351
#else // !LLDB_ENABLE_TERMIOS
352
  return termiosMissingError();
353
#endif // LLDB_ENABLE_TERMIOS
354
5
}
355
356
4
llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {
357
4
#if LLDB_ENABLE_TERMIOS
358
4
  llvm::Expected<Data> data = GetData();
359
4
  if (!data)
360
0
    return data.takeError();
361
362
4
  struct termios &fd_termios = data->m_termios;
363
4
  fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
364
365
4
  if (parity_check != ParityCheck::No) {
366
3
    fd_termios.c_iflag |= INPCK;
367
3
    if (parity_check == ParityCheck::Ignore)
368
1
      fd_termios.c_iflag |= IGNPAR;
369
2
    else if (parity_check == ParityCheck::Mark)
370
1
      fd_termios.c_iflag |= PARMRK;
371
3
  }
372
4
  return SetData(data.get());
373
#else // !LLDB_ENABLE_TERMIOS
374
  return termiosMissingError();
375
#endif // LLDB_ENABLE_TERMIOS
376
4
}
377
378
2
llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
379
2
#if LLDB_ENABLE_TERMIOS
380
2
  llvm::Expected<Data> data = GetData();
381
2
  if (!data)
382
0
    return data.takeError();
383
384
2
#if defined(CRTSCTS)
385
2
  struct termios &fd_termios = data->m_termios;
386
2
  fd_termios.c_cflag &= ~CRTSCTS;
387
2
  if (enabled)
388
1
    fd_termios.c_cflag |= CRTSCTS;
389
2
  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
2
}
401
402
TerminalState::TerminalState(Terminal term, bool save_process_group)
403
5.96k
    : m_tty(term) {
404
5.96k
  Save(term, save_process_group);
405
5.96k
}
406
407
5.95k
TerminalState::~TerminalState() { Restore(); }
408
409
12.7k
void TerminalState::Clear() {
410
12.7k
  m_tty.Clear();
411
12.7k
  m_tflags = -1;
412
12.7k
  m_data.reset();
413
12.7k
  m_process_group = -1;
414
12.7k
}
415
416
6.77k
bool TerminalState::Save(Terminal term, bool save_process_group) {
417
6.77k
  Clear();
418
6.77k
  m_tty = term;
419
6.77k
  if (m_tty.IsATerminal()) {
420
23
    int fd = m_tty.GetFileDescriptor();
421
23
#if LLDB_ENABLE_POSIX
422
23
    m_tflags = ::fcntl(fd, F_GETFL, 0);
423
23
#if LLDB_ENABLE_TERMIOS
424
23
    std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};
425
23
    if (::tcgetattr(fd, &new_data->m_termios) == 0)
426
23
      m_data = std::move(new_data);
427
23
#endif // LLDB_ENABLE_TERMIOS
428
23
    if (save_process_group)
429
13
      m_process_group = ::tcgetpgrp(fd);
430
23
#endif // LLDB_ENABLE_POSIX
431
23
  }
432
6.77k
  return IsValid();
433
6.77k
}
434
435
5.96k
bool TerminalState::Restore() const {
436
5.96k
#if LLDB_ENABLE_POSIX
437
5.96k
  if (IsValid()) {
438
14
    const int fd = m_tty.GetFileDescriptor();
439
14
    if (TFlagsIsValid())
440
14
      fcntl(fd, F_SETFL, m_tflags);
441
442
14
#if LLDB_ENABLE_TERMIOS
443
14
    if (TTYStateIsValid())
444
14
      tcsetattr(fd, TCSANOW, &m_data->m_termios);
445
14
#endif // LLDB_ENABLE_TERMIOS
446
447
14
    if (ProcessGroupIsValid()) {
448
      // Save the original signal handler.
449
0
      void (*saved_sigttou_callback)(int) = nullptr;
450
0
      saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
451
      // Set the process group
452
0
      tcsetpgrp(fd, m_process_group);
453
      // Restore the original signal handler.
454
0
      signal(SIGTTOU, saved_sigttou_callback);
455
0
    }
456
14
    return true;
457
14
  }
458
5.94k
#endif // LLDB_ENABLE_POSIX
459
5.94k
  return false;
460
5.96k
}
461
462
12.7k
bool TerminalState::IsValid() const {
463
12.7k
  return m_tty.FileDescriptorIsValid() &&
464
12.7k
         
(837
TFlagsIsValid()837
||
TTYStateIsValid()800
||
ProcessGroupIsValid()800
);
465
12.7k
}
466
467
851
bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
468
469
814
bool TerminalState::TTYStateIsValid() const { return bool(m_data); }
470
471
814
bool TerminalState::ProcessGroupIsValid() const {
472
814
  return static_cast<int32_t>(m_process_group) != -1;
473
814
}