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 #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 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 } 474