180814287SRaphael Isemann //===-- Terminal.cpp ------------------------------------------------------===//
2a3406614SGreg Clayton //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a3406614SGreg Clayton //
7a3406614SGreg Clayton //===----------------------------------------------------------------------===//
8a3406614SGreg Clayton 
9a3406614SGreg Clayton #include "lldb/Host/Terminal.h"
10f343968fSZachary Turner 
112c77eefeSRaphael Isemann #include "lldb/Host/Config.h"
12f343968fSZachary Turner #include "lldb/Host/PosixApi.h"
1328606954SSaleem Abdulrasool #include "llvm/ADT/STLExtras.h"
14a3406614SGreg Clayton 
1576e47d48SRaphael Isemann #include <csignal>
16a3406614SGreg Clayton #include <fcntl.h>
17a3406614SGreg Clayton 
1861a2bdadSJonas Devlieghere #if LLDB_ENABLE_TERMIOS
19a3406614SGreg Clayton #include <termios.h>
20a3406614SGreg Clayton #endif
21a3406614SGreg Clayton 
22a3406614SGreg Clayton using namespace lldb_private;
23a3406614SGreg Clayton 
2439f2b059SMichał Górny struct Terminal::Data {
2561a2bdadSJonas Devlieghere #if LLDB_ENABLE_TERMIOS
2658b4501eSMichał Górny   struct termios m_termios; ///< Cached terminal state information.
27b2f1fb29SVirgile Bello #endif
2858b4501eSMichał Górny };
2958b4501eSMichał Górny 
IsATerminal() const3039f2b059SMichał Górny bool Terminal::IsATerminal() const { return m_fd >= 0 && ::isatty(m_fd); }
3139f2b059SMichał Górny 
32e9dcd8b3SMichał Górny #if !LLDB_ENABLE_TERMIOS
termiosMissingError()33e9dcd8b3SMichał Górny static llvm::Error termiosMissingError() {
34e9dcd8b3SMichał Górny   return llvm::createStringError(llvm::inconvertibleErrorCode(),
35e9dcd8b3SMichał Górny                                  "termios support missing in LLDB");
36e9dcd8b3SMichał Górny }
37e9dcd8b3SMichał Górny #endif
38e9dcd8b3SMichał Górny 
GetData()3939f2b059SMichał Górny llvm::Expected<Terminal::Data> Terminal::GetData() {
40e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
4139f2b059SMichał Górny   if (!FileDescriptorIsValid())
4239f2b059SMichał Górny     return llvm::createStringError(llvm::inconvertibleErrorCode(),
4339f2b059SMichał Górny                                    "invalid fd");
4439f2b059SMichał Górny 
4539f2b059SMichał Górny   if (!IsATerminal())
4639f2b059SMichał Górny     return llvm::createStringError(llvm::inconvertibleErrorCode(),
4739f2b059SMichał Górny                                    "fd not a terminal");
4839f2b059SMichał Górny 
4939f2b059SMichał Górny   Data data;
5039f2b059SMichał Górny   if (::tcgetattr(m_fd, &data.m_termios) != 0)
5139f2b059SMichał Górny     return llvm::createStringError(
5239f2b059SMichał Górny         std::error_code(errno, std::generic_category()),
5339f2b059SMichał Górny         "unable to get teletype attributes");
5439f2b059SMichał Górny   return data;
5539f2b059SMichał Górny #else // !LLDB_ENABLE_TERMIOS
56e9dcd8b3SMichał Górny   return termiosMissingError();
5739f2b059SMichał Górny #endif // LLDB_ENABLE_TERMIOS
5839f2b059SMichał Górny }
5939f2b059SMichał Górny 
SetData(const Terminal::Data & data)6039f2b059SMichał Górny llvm::Error Terminal::SetData(const Terminal::Data &data) {
6139f2b059SMichał Górny #if LLDB_ENABLE_TERMIOS
6239f2b059SMichał Górny   assert(FileDescriptorIsValid());
6339f2b059SMichał Górny   assert(IsATerminal());
6439f2b059SMichał Górny 
6539f2b059SMichał Górny   if (::tcsetattr(m_fd, TCSANOW, &data.m_termios) != 0)
6639f2b059SMichał Górny     return llvm::createStringError(
6739f2b059SMichał Górny         std::error_code(errno, std::generic_category()),
6839f2b059SMichał Górny         "unable to set teletype attributes");
6939f2b059SMichał Górny   return llvm::Error::success();
7039f2b059SMichał Górny #else // !LLDB_ENABLE_TERMIOS
71e9dcd8b3SMichał Górny   return termiosMissingError();
7239f2b059SMichał Górny #endif // LLDB_ENABLE_TERMIOS
7339f2b059SMichał Górny }
7439f2b059SMichał Górny 
SetEcho(bool enabled)7539f2b059SMichał Górny llvm::Error Terminal::SetEcho(bool enabled) {
76e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
7739f2b059SMichał Górny   llvm::Expected<Data> data = GetData();
7839f2b059SMichał Górny   if (!data)
7939f2b059SMichał Górny     return data.takeError();
8039f2b059SMichał Górny 
8139f2b059SMichał Górny   struct termios &fd_termios = data->m_termios;
8239f2b059SMichał Górny   fd_termios.c_lflag &= ~ECHO;
8339f2b059SMichał Górny   if (enabled)
8439f2b059SMichał Górny     fd_termios.c_lflag |= ECHO;
8539f2b059SMichał Górny   return SetData(data.get());
86e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
87e9dcd8b3SMichał Górny   return termiosMissingError();
8839f2b059SMichał Górny #endif // LLDB_ENABLE_TERMIOS
8939f2b059SMichał Górny }
9039f2b059SMichał Górny 
SetCanonical(bool enabled)9139f2b059SMichał Górny llvm::Error Terminal::SetCanonical(bool enabled) {
92e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
9339f2b059SMichał Górny   llvm::Expected<Data> data = GetData();
9439f2b059SMichał Górny   if (!data)
9539f2b059SMichał Górny     return data.takeError();
9639f2b059SMichał Górny 
9739f2b059SMichał Górny   struct termios &fd_termios = data->m_termios;
9839f2b059SMichał Górny   fd_termios.c_lflag &= ~ICANON;
9939f2b059SMichał Górny   if (enabled)
10039f2b059SMichał Górny     fd_termios.c_lflag |= ICANON;
10139f2b059SMichał Górny   return SetData(data.get());
102e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
103e9dcd8b3SMichał Górny   return termiosMissingError();
10439f2b059SMichał Górny #endif // LLDB_ENABLE_TERMIOS
10539f2b059SMichał Górny }
10639f2b059SMichał Górny 
SetRaw()10792fb574cSMichał Górny llvm::Error Terminal::SetRaw() {
108e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
10992fb574cSMichał Górny   llvm::Expected<Data> data = GetData();
11092fb574cSMichał Górny   if (!data)
11192fb574cSMichał Górny     return data.takeError();
11292fb574cSMichał Górny 
11392fb574cSMichał Górny   struct termios &fd_termios = data->m_termios;
11492fb574cSMichał Górny   ::cfmakeraw(&fd_termios);
11592fb574cSMichał Górny 
11692fb574cSMichał Górny   // Make sure only one character is needed to return from a read
11792fb574cSMichał Górny   // (cfmakeraw() doesn't do this on NetBSD)
11892fb574cSMichał Górny   fd_termios.c_cc[VMIN] = 1;
11992fb574cSMichał Górny   fd_termios.c_cc[VTIME] = 0;
12092fb574cSMichał Górny 
12192fb574cSMichał Górny   return SetData(data.get());
122e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
123e9dcd8b3SMichał Górny   return termiosMissingError();
124e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
12592fb574cSMichał Górny }
12692fb574cSMichał Górny 
127cbe78984SMichał Górny #if LLDB_ENABLE_TERMIOS
baudRateToConst(unsigned int baud_rate)12892fb574cSMichał Górny static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
12992fb574cSMichał Górny   switch (baud_rate) {
13092fb574cSMichał Górny #if defined(B50)
13192fb574cSMichał Górny   case 50:
13292fb574cSMichał Górny     return B50;
13392fb574cSMichał Górny #endif
13492fb574cSMichał Górny #if defined(B75)
13592fb574cSMichał Górny   case 75:
13692fb574cSMichał Górny     return B75;
13792fb574cSMichał Górny #endif
13892fb574cSMichał Górny #if defined(B110)
13992fb574cSMichał Górny   case 110:
14092fb574cSMichał Górny     return B110;
14192fb574cSMichał Górny #endif
14292fb574cSMichał Górny #if defined(B134)
14392fb574cSMichał Górny   case 134:
14492fb574cSMichał Górny     return B134;
14592fb574cSMichał Górny #endif
14692fb574cSMichał Górny #if defined(B150)
14792fb574cSMichał Górny   case 150:
14892fb574cSMichał Górny     return B150;
14992fb574cSMichał Górny #endif
15092fb574cSMichał Górny #if defined(B200)
15192fb574cSMichał Górny   case 200:
15292fb574cSMichał Górny     return B200;
15392fb574cSMichał Górny #endif
15492fb574cSMichał Górny #if defined(B300)
15592fb574cSMichał Górny   case 300:
15692fb574cSMichał Górny     return B300;
15792fb574cSMichał Górny #endif
15892fb574cSMichał Górny #if defined(B600)
15992fb574cSMichał Górny   case 600:
16092fb574cSMichał Górny     return B600;
16192fb574cSMichał Górny #endif
16292fb574cSMichał Górny #if defined(B1200)
16392fb574cSMichał Górny   case 1200:
16492fb574cSMichał Górny     return B1200;
16592fb574cSMichał Górny #endif
16692fb574cSMichał Górny #if defined(B1800)
16792fb574cSMichał Górny   case 1800:
16892fb574cSMichał Górny     return B1800;
16992fb574cSMichał Górny #endif
17092fb574cSMichał Górny #if defined(B2400)
17192fb574cSMichał Górny   case 2400:
17292fb574cSMichał Górny     return B2400;
17392fb574cSMichał Górny #endif
17492fb574cSMichał Górny #if defined(B4800)
17592fb574cSMichał Górny   case 4800:
17692fb574cSMichał Górny     return B4800;
17792fb574cSMichał Górny #endif
17892fb574cSMichał Górny #if defined(B9600)
17992fb574cSMichał Górny   case 9600:
18092fb574cSMichał Górny     return B9600;
18192fb574cSMichał Górny #endif
18292fb574cSMichał Górny #if defined(B19200)
18392fb574cSMichał Górny   case 19200:
18492fb574cSMichał Górny     return B19200;
18592fb574cSMichał Górny #endif
18692fb574cSMichał Górny #if defined(B38400)
18792fb574cSMichał Górny   case 38400:
18892fb574cSMichał Górny     return B38400;
18992fb574cSMichał Górny #endif
19092fb574cSMichał Górny #if defined(B57600)
19192fb574cSMichał Górny   case 57600:
19292fb574cSMichał Górny     return B57600;
19392fb574cSMichał Górny #endif
19492fb574cSMichał Górny #if defined(B115200)
19592fb574cSMichał Górny   case 115200:
19692fb574cSMichał Górny     return B115200;
19792fb574cSMichał Górny #endif
19892fb574cSMichał Górny #if defined(B230400)
19992fb574cSMichał Górny   case 230400:
20092fb574cSMichał Górny     return B230400;
20192fb574cSMichał Górny #endif
20292fb574cSMichał Górny #if defined(B460800)
20392fb574cSMichał Górny   case 460800:
20492fb574cSMichał Górny     return B460800;
20592fb574cSMichał Górny #endif
20692fb574cSMichał Górny #if defined(B500000)
20792fb574cSMichał Górny   case 500000:
20892fb574cSMichał Górny     return B500000;
20992fb574cSMichał Górny #endif
21092fb574cSMichał Górny #if defined(B576000)
21192fb574cSMichał Górny   case 576000:
21292fb574cSMichał Górny     return B576000;
21392fb574cSMichał Górny #endif
21492fb574cSMichał Górny #if defined(B921600)
21592fb574cSMichał Górny   case 921600:
21692fb574cSMichał Górny     return B921600;
21792fb574cSMichał Górny #endif
21892fb574cSMichał Górny #if defined(B1000000)
21992fb574cSMichał Górny   case 1000000:
22092fb574cSMichał Górny     return B1000000;
22192fb574cSMichał Górny #endif
22292fb574cSMichał Górny #if defined(B1152000)
22392fb574cSMichał Górny   case 1152000:
22492fb574cSMichał Górny     return B1152000;
22592fb574cSMichał Górny #endif
22692fb574cSMichał Górny #if defined(B1500000)
22792fb574cSMichał Górny   case 1500000:
22892fb574cSMichał Górny     return B1500000;
22992fb574cSMichał Górny #endif
23092fb574cSMichał Górny #if defined(B2000000)
23192fb574cSMichał Górny   case 2000000:
23292fb574cSMichał Górny     return B2000000;
23392fb574cSMichał Górny #endif
23492fb574cSMichał Górny #if defined(B76800)
23592fb574cSMichał Górny   case 76800:
23692fb574cSMichał Górny     return B76800;
23792fb574cSMichał Górny #endif
23892fb574cSMichał Górny #if defined(B153600)
23992fb574cSMichał Górny   case 153600:
24092fb574cSMichał Górny     return B153600;
24192fb574cSMichał Górny #endif
24292fb574cSMichał Górny #if defined(B307200)
24392fb574cSMichał Górny   case 307200:
24492fb574cSMichał Górny     return B307200;
24592fb574cSMichał Górny #endif
24692fb574cSMichał Górny #if defined(B614400)
24792fb574cSMichał Górny   case 614400:
24892fb574cSMichał Górny     return B614400;
24992fb574cSMichał Górny #endif
25092fb574cSMichał Górny #if defined(B2500000)
25192fb574cSMichał Górny   case 2500000:
25292fb574cSMichał Górny     return B2500000;
25392fb574cSMichał Górny #endif
25492fb574cSMichał Górny #if defined(B3000000)
25592fb574cSMichał Górny   case 3000000:
25692fb574cSMichał Górny     return B3000000;
25792fb574cSMichał Górny #endif
25892fb574cSMichał Górny #if defined(B3500000)
25992fb574cSMichał Górny   case 3500000:
26092fb574cSMichał Górny     return B3500000;
26192fb574cSMichał Górny #endif
26292fb574cSMichał Górny #if defined(B4000000)
26392fb574cSMichał Górny   case 4000000:
26492fb574cSMichał Górny     return B4000000;
26592fb574cSMichał Górny #endif
26692fb574cSMichał Górny   default:
26792fb574cSMichał Górny     return llvm::None;
26892fb574cSMichał Górny   }
26992fb574cSMichał Górny }
270cbe78984SMichał Górny #endif
27192fb574cSMichał Górny 
SetBaudRate(unsigned int baud_rate)27292fb574cSMichał Górny llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
273e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
27492fb574cSMichał Górny   llvm::Expected<Data> data = GetData();
27592fb574cSMichał Górny   if (!data)
27692fb574cSMichał Górny     return data.takeError();
27792fb574cSMichał Górny 
27892fb574cSMichał Górny   struct termios &fd_termios = data->m_termios;
27992fb574cSMichał Górny   llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
28092fb574cSMichał Górny   if (!val) // invalid value
28192fb574cSMichał Górny     return llvm::createStringError(llvm::inconvertibleErrorCode(),
28292fb574cSMichał Górny                                    "baud rate %d unsupported by the platform",
28392fb574cSMichał Górny                                    baud_rate);
284*5cff5142SKazu Hirata   if (::cfsetispeed(&fd_termios, val.value()) != 0)
28592fb574cSMichał Górny     return llvm::createStringError(
28692fb574cSMichał Górny         std::error_code(errno, std::generic_category()),
28792fb574cSMichał Górny         "setting input baud rate failed");
288*5cff5142SKazu Hirata   if (::cfsetospeed(&fd_termios, val.value()) != 0)
28992fb574cSMichał Górny     return llvm::createStringError(
29092fb574cSMichał Górny         std::error_code(errno, std::generic_category()),
29192fb574cSMichał Górny         "setting output baud rate failed");
29292fb574cSMichał Górny   return SetData(data.get());
293e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
294e9dcd8b3SMichał Górny   return termiosMissingError();
295e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
29692fb574cSMichał Górny }
29792fb574cSMichał Górny 
SetStopBits(unsigned int stop_bits)29892fb574cSMichał Górny llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
299e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
30092fb574cSMichał Górny   llvm::Expected<Data> data = GetData();
30192fb574cSMichał Górny   if (!data)
30292fb574cSMichał Górny     return data.takeError();
30392fb574cSMichał Górny 
30492fb574cSMichał Górny   struct termios &fd_termios = data->m_termios;
30592fb574cSMichał Górny   switch (stop_bits) {
30692fb574cSMichał Górny   case 1:
30792fb574cSMichał Górny     fd_termios.c_cflag &= ~CSTOPB;
30892fb574cSMichał Górny     break;
30992fb574cSMichał Górny   case 2:
31092fb574cSMichał Górny     fd_termios.c_cflag |= CSTOPB;
31192fb574cSMichał Górny     break;
31292fb574cSMichał Górny   default:
31392fb574cSMichał Górny     return llvm::createStringError(
31492fb574cSMichał Górny         llvm::inconvertibleErrorCode(),
31592fb574cSMichał Górny         "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
31692fb574cSMichał Górny   }
31792fb574cSMichał Górny   return SetData(data.get());
318e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
319e9dcd8b3SMichał Górny   return termiosMissingError();
320e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
32192fb574cSMichał Górny }
32292fb574cSMichał Górny 
SetParity(Terminal::Parity parity)32392fb574cSMichał Górny llvm::Error Terminal::SetParity(Terminal::Parity parity) {
324e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
32592fb574cSMichał Górny   llvm::Expected<Data> data = GetData();
32692fb574cSMichał Górny   if (!data)
32792fb574cSMichał Górny     return data.takeError();
32892fb574cSMichał Górny 
32992fb574cSMichał Górny   struct termios &fd_termios = data->m_termios;
33092fb574cSMichał Górny   fd_termios.c_cflag &= ~(
33192fb574cSMichał Górny #if defined(CMSPAR)
33292fb574cSMichał Górny       CMSPAR |
33392fb574cSMichał Górny #endif
33492fb574cSMichał Górny       PARENB | PARODD);
33592fb574cSMichał Górny 
33692fb574cSMichał Górny   if (parity != Parity::No) {
33792fb574cSMichał Górny     fd_termios.c_cflag |= PARENB;
33892fb574cSMichał Górny     if (parity == Parity::Odd || parity == Parity::Mark)
33992fb574cSMichał Górny       fd_termios.c_cflag |= PARODD;
34092fb574cSMichał Górny     if (parity == Parity::Mark || parity == Parity::Space) {
34192fb574cSMichał Górny #if defined(CMSPAR)
34292fb574cSMichał Górny       fd_termios.c_cflag |= CMSPAR;
34392fb574cSMichał Górny #else
34492fb574cSMichał Górny       return llvm::createStringError(
34592fb574cSMichał Górny           llvm::inconvertibleErrorCode(),
34692fb574cSMichał Górny           "space/mark parity is not supported by the platform");
34792fb574cSMichał Górny #endif
34892fb574cSMichał Górny     }
34992fb574cSMichał Górny   }
35092fb574cSMichał Górny   return SetData(data.get());
351e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
352e9dcd8b3SMichał Górny   return termiosMissingError();
353e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
35492fb574cSMichał Górny }
35592fb574cSMichał Górny 
SetParityCheck(Terminal::ParityCheck parity_check)35621bb808eSMichał Górny llvm::Error Terminal::SetParityCheck(Terminal::ParityCheck parity_check) {
357e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
35821bb808eSMichał Górny   llvm::Expected<Data> data = GetData();
35921bb808eSMichał Górny   if (!data)
36021bb808eSMichał Górny     return data.takeError();
36121bb808eSMichał Górny 
36221bb808eSMichał Górny   struct termios &fd_termios = data->m_termios;
36321bb808eSMichał Górny   fd_termios.c_iflag &= ~(IGNPAR | PARMRK | INPCK);
36421bb808eSMichał Górny 
36521bb808eSMichał Górny   if (parity_check != ParityCheck::No) {
36621bb808eSMichał Górny     fd_termios.c_iflag |= INPCK;
36721bb808eSMichał Górny     if (parity_check == ParityCheck::Ignore)
36821bb808eSMichał Górny       fd_termios.c_iflag |= IGNPAR;
36921bb808eSMichał Górny     else if (parity_check == ParityCheck::Mark)
37021bb808eSMichał Górny       fd_termios.c_iflag |= PARMRK;
37121bb808eSMichał Górny   }
37221bb808eSMichał Górny   return SetData(data.get());
373e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
374e9dcd8b3SMichał Górny   return termiosMissingError();
375e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
37621bb808eSMichał Górny }
37721bb808eSMichał Górny 
SetHardwareFlowControl(bool enabled)37892fb574cSMichał Górny llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
379e9dcd8b3SMichał Górny #if LLDB_ENABLE_TERMIOS
38092fb574cSMichał Górny   llvm::Expected<Data> data = GetData();
38192fb574cSMichał Górny   if (!data)
38292fb574cSMichał Górny     return data.takeError();
38392fb574cSMichał Górny 
38492fb574cSMichał Górny #if defined(CRTSCTS)
38592fb574cSMichał Górny   struct termios &fd_termios = data->m_termios;
38692fb574cSMichał Górny   fd_termios.c_cflag &= ~CRTSCTS;
38792fb574cSMichał Górny   if (enabled)
38892fb574cSMichał Górny     fd_termios.c_cflag |= CRTSCTS;
38992fb574cSMichał Górny   return SetData(data.get());
39092fb574cSMichał Górny #else  // !defined(CRTSCTS)
39192fb574cSMichał Górny   if (enabled)
39292fb574cSMichał Górny     return llvm::createStringError(
39392fb574cSMichał Górny         llvm::inconvertibleErrorCode(),
39492fb574cSMichał Górny         "hardware flow control is not supported by the platform");
39592fb574cSMichał Górny   return llvm::Error::success();
39692fb574cSMichał Górny #endif // defined(CRTSCTS)
397e9dcd8b3SMichał Górny #else // !LLDB_ENABLE_TERMIOS
398e9dcd8b3SMichał Górny   return termiosMissingError();
399e9dcd8b3SMichał Górny #endif // LLDB_ENABLE_TERMIOS
40092fb574cSMichał Górny }
40192fb574cSMichał Górny 
TerminalState(Terminal term,bool save_process_group)40258b4501eSMichał Górny TerminalState::TerminalState(Terminal term, bool save_process_group)
40358b4501eSMichał Górny     : m_tty(term) {
40458b4501eSMichał Górny   Save(term, save_process_group);
405a3406614SGreg Clayton }
406a3406614SGreg Clayton 
~TerminalState()40758b4501eSMichał Górny TerminalState::~TerminalState() { Restore(); }
408a3406614SGreg Clayton 
Clear()409b9c1b51eSKate Stone void TerminalState::Clear() {
410c5917d9aSJim Ingham   m_tty.Clear();
411c5917d9aSJim Ingham   m_tflags = -1;
41258b4501eSMichał Górny   m_data.reset();
413c5917d9aSJim Ingham   m_process_group = -1;
414c5917d9aSJim Ingham }
415c5917d9aSJim Ingham 
Save(Terminal term,bool save_process_group)41658b4501eSMichał Górny bool TerminalState::Save(Terminal term, bool save_process_group) {
41758b4501eSMichał Górny   Clear();
41858b4501eSMichał Górny   m_tty = term;
419b9c1b51eSKate Stone   if (m_tty.IsATerminal()) {
4203011d55fSJonas Devlieghere #if LLDB_ENABLE_POSIX
421d13847bbSSimon Pilgrim     int fd = m_tty.GetFileDescriptor();
422a3406614SGreg Clayton     m_tflags = ::fcntl(fd, F_GETFL, 0);
42361a2bdadSJonas Devlieghere #if LLDB_ENABLE_TERMIOS
42439f2b059SMichał Górny     std::unique_ptr<Terminal::Data> new_data{new Terminal::Data()};
425bd21257bSMichał Górny     if (::tcgetattr(fd, &new_data->m_termios) == 0)
42658b4501eSMichał Górny       m_data = std::move(new_data);
42758b4501eSMichał Górny #endif // LLDB_ENABLE_TERMIOS
428a3406614SGreg Clayton     if (save_process_group)
42958b4501eSMichał Górny       m_process_group = ::tcgetpgrp(fd);
43058b4501eSMichał Górny #endif // LLDB_ENABLE_POSIX
431a3406614SGreg Clayton   }
432a3406614SGreg Clayton   return IsValid();
433a3406614SGreg Clayton }
434a3406614SGreg Clayton 
Restore() const435b9c1b51eSKate Stone bool TerminalState::Restore() const {
4363011d55fSJonas Devlieghere #if LLDB_ENABLE_POSIX
437b9c1b51eSKate Stone   if (IsValid()) {
438a3406614SGreg Clayton     const int fd = m_tty.GetFileDescriptor();
439a3406614SGreg Clayton     if (TFlagsIsValid())
44023f59509SGreg Clayton       fcntl(fd, F_SETFL, m_tflags);
441a3406614SGreg Clayton 
44261a2bdadSJonas Devlieghere #if LLDB_ENABLE_TERMIOS
443a3406614SGreg Clayton     if (TTYStateIsValid())
44458b4501eSMichał Górny       tcsetattr(fd, TCSANOW, &m_data->m_termios);
44558b4501eSMichał Górny #endif // LLDB_ENABLE_TERMIOS
446a3406614SGreg Clayton 
447b9c1b51eSKate Stone     if (ProcessGroupIsValid()) {
448a3406614SGreg Clayton       // Save the original signal handler.
449248a1305SKonrad Kleine       void (*saved_sigttou_callback)(int) = nullptr;
450a3406614SGreg Clayton       saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
451a3406614SGreg Clayton       // Set the process group
45223f59509SGreg Clayton       tcsetpgrp(fd, m_process_group);
453a3406614SGreg Clayton       // Restore the original signal handler.
454a3406614SGreg Clayton       signal(SIGTTOU, saved_sigttou_callback);
455a3406614SGreg Clayton     }
456a3406614SGreg Clayton     return true;
457a3406614SGreg Clayton   }
45858b4501eSMichał Górny #endif // LLDB_ENABLE_POSIX
459a3406614SGreg Clayton   return false;
460a3406614SGreg Clayton }
461a3406614SGreg Clayton 
IsValid() const462b9c1b51eSKate Stone bool TerminalState::IsValid() const {
463b9c1b51eSKate Stone   return m_tty.FileDescriptorIsValid() &&
46458b4501eSMichał Górny          (TFlagsIsValid() || TTYStateIsValid() || ProcessGroupIsValid());
465a3406614SGreg Clayton }
466a3406614SGreg Clayton 
TFlagsIsValid() const467b9c1b51eSKate Stone bool TerminalState::TFlagsIsValid() const { return m_tflags != -1; }
468a3406614SGreg Clayton 
TTYStateIsValid() const46958b4501eSMichał Górny bool TerminalState::TTYStateIsValid() const { return bool(m_data); }
470a3406614SGreg Clayton 
ProcessGroupIsValid() const471b9c1b51eSKate Stone bool TerminalState::ProcessGroupIsValid() const {
4723985c8c6SSaleem Abdulrasool   return static_cast<int32_t>(m_process_group) != -1;
473a3406614SGreg Clayton }
474