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