1//===-- TTYState.cpp --------------------------------------------*- C++ -*-===//
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//  Created by Greg Clayton on 3/26/07.
10//
11//===----------------------------------------------------------------------===//
12
13#include "TTYState.h"
14#include <fcntl.h>
15#include <sys/signal.h>
16#include <unistd.h>
17
18TTYState::TTYState()
19    : m_fd(-1), m_tflags(-1), m_ttystateErr(-1), m_processGroup(-1) {}
20
21TTYState::~TTYState() = default;
22
23bool TTYState::GetTTYState(int fd, bool saveProcessGroup) {
24  if (fd >= 0 && ::isatty(fd)) {
25    m_fd = fd;
26    m_tflags = fcntl(fd, F_GETFL, 0);
27    m_ttystateErr = tcgetattr(fd, &m_ttystate);
28    if (saveProcessGroup)
29      m_processGroup = tcgetpgrp(0);
30    else
31      m_processGroup = -1;
32  } else {
33    m_fd = -1;
34    m_tflags = -1;
35    m_ttystateErr = -1;
36    m_processGroup = -1;
37  }
38  return m_ttystateErr == 0;
39}
40
41bool TTYState::SetTTYState() const {
42  if (IsValid()) {
43    if (TFlagsValid())
44      fcntl(m_fd, F_SETFL, m_tflags);
45
46    if (TTYStateValid())
47      tcsetattr(m_fd, TCSANOW, &m_ttystate);
48
49    if (ProcessGroupValid()) {
50      // Save the original signal handler.
51      void (*saved_sigttou_callback)(int) = NULL;
52      saved_sigttou_callback = (void (*)(int))signal(SIGTTOU, SIG_IGN);
53      // Set the process group
54      tcsetpgrp(m_fd, m_processGroup);
55      // Restore the original signal handler.
56      signal(SIGTTOU, saved_sigttou_callback);
57    }
58    return true;
59  }
60  return false;
61}
62
63TTYStateSwitcher::TTYStateSwitcher() : m_currentState(~0) {}
64
65TTYStateSwitcher::~TTYStateSwitcher() = default;
66
67bool TTYStateSwitcher::GetState(uint32_t idx, int fd, bool saveProcessGroup) {
68  if (ValidStateIndex(idx))
69    return m_ttystates[idx].GetTTYState(fd, saveProcessGroup);
70  return false;
71}
72
73bool TTYStateSwitcher::SetState(uint32_t idx) const {
74  if (!ValidStateIndex(idx))
75    return false;
76
77  // See if we already are in this state?
78  if (ValidStateIndex(m_currentState) && (idx == m_currentState) &&
79      m_ttystates[idx].IsValid())
80    return true;
81
82  // Set the state to match the index passed in and only update the
83  // current state if there are no errors.
84  if (m_ttystates[idx].SetTTYState()) {
85    m_currentState = idx;
86    return true;
87  }
88
89  // We failed to set the state. The tty state was invalid or not
90  // initialized.
91  return false;
92}
93