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