1317027Sdim//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
2317027Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6317027Sdim//
7317027Sdim//===----------------------------------------------------------------------===//
8317027Sdim
9317027Sdim#include "lldb/Host/PseudoTerminal.h"
10317027Sdim#include "lldb/Host/Config.h"
11317027Sdim
12344779Sdim#include "llvm/Support/Errno.h"
13344779Sdim
14317027Sdim#include <stdio.h>
15317027Sdim#include <stdlib.h>
16317027Sdim#include <string.h>
17317027Sdim#if defined(TIOCSCTTY)
18317027Sdim#include <sys/ioctl.h>
19317027Sdim#endif
20317027Sdim
21317027Sdim#include "lldb/Host/PosixApi.h"
22317027Sdim
23317027Sdim#if defined(__ANDROID__)
24317027Sdimint posix_openpt(int flags);
25317027Sdim#endif
26317027Sdim
27327952Sdimusing namespace lldb_private;
28317027Sdim
29344779Sdim// Write string describing error number
30344779Sdimstatic void ErrnoToStr(char *error_str, size_t error_len) {
31344779Sdim  std::string strerror = llvm::sys::StrError();
32344779Sdim  ::snprintf(error_str, error_len, "%s", strerror.c_str());
33344779Sdim}
34344779Sdim
35317027Sdim// PseudoTerminal constructor
36317027SdimPseudoTerminal::PseudoTerminal()
37317027Sdim    : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {}
38317027Sdim
39317027Sdim// Destructor
40317027Sdim//
41341825Sdim// The destructor will close the master and slave file descriptors if they are
42341825Sdim// valid and ownership has not been released using the
43341825Sdim// ReleaseMasterFileDescriptor() or the ReleaseSaveFileDescriptor() member
44341825Sdim// functions.
45317027SdimPseudoTerminal::~PseudoTerminal() {
46317027Sdim  CloseMasterFileDescriptor();
47317027Sdim  CloseSlaveFileDescriptor();
48317027Sdim}
49317027Sdim
50317027Sdim// Close the master file descriptor if it is valid.
51317027Sdimvoid PseudoTerminal::CloseMasterFileDescriptor() {
52317027Sdim  if (m_master_fd >= 0) {
53317027Sdim    ::close(m_master_fd);
54317027Sdim    m_master_fd = invalid_fd;
55317027Sdim  }
56317027Sdim}
57317027Sdim
58317027Sdim// Close the slave file descriptor if it is valid.
59317027Sdimvoid PseudoTerminal::CloseSlaveFileDescriptor() {
60317027Sdim  if (m_slave_fd >= 0) {
61317027Sdim    ::close(m_slave_fd);
62317027Sdim    m_slave_fd = invalid_fd;
63317027Sdim  }
64317027Sdim}
65317027Sdim
66341825Sdim// Open the first available pseudo terminal with OFLAG as the permissions. The
67341825Sdim// file descriptor is stored in this object and can be accessed with the
68341825Sdim// MasterFileDescriptor() accessor. The ownership of the master file descriptor
69341825Sdim// can be released using the ReleaseMasterFileDescriptor() accessor. If this
70341825Sdim// object has a valid master files descriptor when its destructor is called, it
71341825Sdim// will close the master file descriptor, therefore clients must call
72341825Sdim// ReleaseMasterFileDescriptor() if they wish to use the master file descriptor
73341825Sdim// after this object is out of scope or destroyed.
74317027Sdim//
75317027Sdim// RETURNS:
76317027Sdim//  True when successful, false indicating an error occurred.
77317027Sdimbool PseudoTerminal::OpenFirstAvailableMaster(int oflag, char *error_str,
78317027Sdim                                              size_t error_len) {
79317027Sdim  if (error_str)
80317027Sdim    error_str[0] = '\0';
81317027Sdim
82360784Sdim#if LLDB_ENABLE_POSIX
83317027Sdim  // Open the master side of a pseudo terminal
84317027Sdim  m_master_fd = ::posix_openpt(oflag);
85317027Sdim  if (m_master_fd < 0) {
86317027Sdim    if (error_str)
87344779Sdim      ErrnoToStr(error_str, error_len);
88317027Sdim    return false;
89317027Sdim  }
90317027Sdim
91317027Sdim  // Grant access to the slave pseudo terminal
92317027Sdim  if (::grantpt(m_master_fd) < 0) {
93317027Sdim    if (error_str)
94344779Sdim      ErrnoToStr(error_str, error_len);
95317027Sdim    CloseMasterFileDescriptor();
96317027Sdim    return false;
97317027Sdim  }
98317027Sdim
99317027Sdim  // Clear the lock flag on the slave pseudo terminal
100317027Sdim  if (::unlockpt(m_master_fd) < 0) {
101317027Sdim    if (error_str)
102344779Sdim      ErrnoToStr(error_str, error_len);
103317027Sdim    CloseMasterFileDescriptor();
104317027Sdim    return false;
105317027Sdim  }
106317027Sdim
107317027Sdim  return true;
108317027Sdim#else
109317027Sdim  if (error_str)
110317027Sdim    ::snprintf(error_str, error_len, "%s", "pseudo terminal not supported");
111317027Sdim  return false;
112317027Sdim#endif
113317027Sdim}
114317027Sdim
115341825Sdim// Open the slave pseudo terminal for the current master pseudo terminal. A
116341825Sdim// master pseudo terminal should already be valid prior to calling this
117341825Sdim// function (see OpenFirstAvailableMaster()). The file descriptor is stored
118341825Sdim// this object's member variables and can be accessed via the
119341825Sdim// GetSlaveFileDescriptor(), or released using the ReleaseSlaveFileDescriptor()
120341825Sdim// member function.
121317027Sdim//
122317027Sdim// RETURNS:
123317027Sdim//  True when successful, false indicating an error occurred.
124317027Sdimbool PseudoTerminal::OpenSlave(int oflag, char *error_str, size_t error_len) {
125317027Sdim  if (error_str)
126317027Sdim    error_str[0] = '\0';
127317027Sdim
128317027Sdim  CloseSlaveFileDescriptor();
129317027Sdim
130317027Sdim  // Open the master side of a pseudo terminal
131317027Sdim  const char *slave_name = GetSlaveName(error_str, error_len);
132317027Sdim
133317027Sdim  if (slave_name == nullptr)
134317027Sdim    return false;
135317027Sdim
136353358Sdim  m_slave_fd = llvm::sys::RetryAfterSignal(-1, ::open, slave_name, oflag);
137317027Sdim
138317027Sdim  if (m_slave_fd < 0) {
139317027Sdim    if (error_str)
140344779Sdim      ErrnoToStr(error_str, error_len);
141317027Sdim    return false;
142317027Sdim  }
143317027Sdim
144317027Sdim  return true;
145317027Sdim}
146317027Sdim
147341825Sdim// Get the name of the slave pseudo terminal. A master pseudo terminal should
148341825Sdim// already be valid prior to calling this function (see
149317027Sdim// OpenFirstAvailableMaster()).
150317027Sdim//
151317027Sdim// RETURNS:
152317027Sdim//  NULL if no valid master pseudo terminal or if ptsname() fails.
153317027Sdim//  The name of the slave pseudo terminal as a NULL terminated C string
154317027Sdim//  that comes from static memory, so a copy of the string should be
155317027Sdim//  made as subsequent calls can change this value.
156317027Sdimconst char *PseudoTerminal::GetSlaveName(char *error_str,
157317027Sdim                                         size_t error_len) const {
158317027Sdim  if (error_str)
159317027Sdim    error_str[0] = '\0';
160317027Sdim
161317027Sdim  if (m_master_fd < 0) {
162317027Sdim    if (error_str)
163317027Sdim      ::snprintf(error_str, error_len, "%s",
164317027Sdim                 "master file descriptor is invalid");
165317027Sdim    return nullptr;
166317027Sdim  }
167317027Sdim  const char *slave_name = ::ptsname(m_master_fd);
168317027Sdim
169317027Sdim  if (error_str && slave_name == nullptr)
170344779Sdim    ErrnoToStr(error_str, error_len);
171317027Sdim
172317027Sdim  return slave_name;
173317027Sdim}
174317027Sdim
175317027Sdim// Fork a child process and have its stdio routed to a pseudo terminal.
176317027Sdim//
177317027Sdim// In the parent process when a valid pid is returned, the master file
178341825Sdim// descriptor can be used as a read/write access to stdio of the child process.
179317027Sdim//
180341825Sdim// In the child process the stdin/stdout/stderr will already be routed to the
181341825Sdim// slave pseudo terminal and the master file descriptor will be closed as it is
182341825Sdim// no longer needed by the child process.
183317027Sdim//
184341825Sdim// This class will close the file descriptors for the master/slave when the
185341825Sdim// destructor is called, so be sure to call ReleaseMasterFileDescriptor() or
186341825Sdim// ReleaseSlaveFileDescriptor() if any file descriptors are going to be used
187341825Sdim// past the lifespan of this object.
188317027Sdim//
189317027Sdim// RETURNS:
190317027Sdim//  in the parent process: the pid of the child, or -1 if fork fails
191317027Sdim//  in the child process: zero
192317027Sdimlldb::pid_t PseudoTerminal::Fork(char *error_str, size_t error_len) {
193317027Sdim  if (error_str)
194317027Sdim    error_str[0] = '\0';
195317027Sdim  pid_t pid = LLDB_INVALID_PROCESS_ID;
196360784Sdim#if LLDB_ENABLE_POSIX
197317027Sdim  int flags = O_RDWR;
198317027Sdim  flags |= O_CLOEXEC;
199317027Sdim  if (OpenFirstAvailableMaster(flags, error_str, error_len)) {
200317027Sdim    // Successfully opened our master pseudo terminal
201317027Sdim
202317027Sdim    pid = ::fork();
203317027Sdim    if (pid < 0) {
204317027Sdim      // Fork failed
205317027Sdim      if (error_str)
206344779Sdim        ErrnoToStr(error_str, error_len);
207317027Sdim    } else if (pid == 0) {
208317027Sdim      // Child Process
209317027Sdim      ::setsid();
210317027Sdim
211317027Sdim      if (OpenSlave(O_RDWR, error_str, error_len)) {
212317027Sdim        // Successfully opened slave
213317027Sdim
214317027Sdim        // Master FD should have O_CLOEXEC set, but let's close it just in
215317027Sdim        // case...
216317027Sdim        CloseMasterFileDescriptor();
217317027Sdim
218317027Sdim#if defined(TIOCSCTTY)
219317027Sdim        // Acquire the controlling terminal
220317027Sdim        if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0) {
221317027Sdim          if (error_str)
222344779Sdim            ErrnoToStr(error_str, error_len);
223317027Sdim        }
224317027Sdim#endif
225317027Sdim        // Duplicate all stdio file descriptors to the slave pseudo terminal
226317027Sdim        if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO) {
227317027Sdim          if (error_str && !error_str[0])
228344779Sdim            ErrnoToStr(error_str, error_len);
229317027Sdim        }
230317027Sdim
231317027Sdim        if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO) {
232317027Sdim          if (error_str && !error_str[0])
233344779Sdim            ErrnoToStr(error_str, error_len);
234317027Sdim        }
235317027Sdim
236317027Sdim        if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO) {
237317027Sdim          if (error_str && !error_str[0])
238344779Sdim            ErrnoToStr(error_str, error_len);
239317027Sdim        }
240317027Sdim      }
241317027Sdim    } else {
242317027Sdim      // Parent Process
243317027Sdim      // Do nothing and let the pid get returned!
244317027Sdim    }
245317027Sdim  }
246317027Sdim#endif
247317027Sdim  return pid;
248317027Sdim}
249317027Sdim
250341825Sdim// The master file descriptor accessor. This object retains ownership of the
251341825Sdim// master file descriptor when this accessor is used. Use
252341825Sdim// ReleaseMasterFileDescriptor() if you wish this object to release ownership
253341825Sdim// of the master file descriptor.
254317027Sdim//
255341825Sdim// Returns the master file descriptor, or -1 if the master file descriptor is
256341825Sdim// not currently valid.
257317027Sdimint PseudoTerminal::GetMasterFileDescriptor() const { return m_master_fd; }
258317027Sdim
259317027Sdim// The slave file descriptor accessor.
260317027Sdim//
261341825Sdim// Returns the slave file descriptor, or -1 if the slave file descriptor is not
262341825Sdim// currently valid.
263317027Sdimint PseudoTerminal::GetSlaveFileDescriptor() const { return m_slave_fd; }
264317027Sdim
265341825Sdim// Release ownership of the master pseudo terminal file descriptor without
266341825Sdim// closing it. The destructor for this class will close the master file
267341825Sdim// descriptor if the ownership isn't released using this call and the master
268341825Sdim// file descriptor has been opened.
269317027Sdimint PseudoTerminal::ReleaseMasterFileDescriptor() {
270341825Sdim  // Release ownership of the master pseudo terminal file descriptor without
271341825Sdim  // closing it. (the destructor for this class will close it otherwise!)
272317027Sdim  int fd = m_master_fd;
273317027Sdim  m_master_fd = invalid_fd;
274317027Sdim  return fd;
275317027Sdim}
276317027Sdim
277341825Sdim// Release ownership of the slave pseudo terminal file descriptor without
278341825Sdim// closing it. The destructor for this class will close the slave file
279341825Sdim// descriptor if the ownership isn't released using this call and the slave
280341825Sdim// file descriptor has been opened.
281317027Sdimint PseudoTerminal::ReleaseSlaveFileDescriptor() {
282341825Sdim  // Release ownership of the slave pseudo terminal file descriptor without
283341825Sdim  // closing it (the destructor for this class will close it otherwise!)
284317027Sdim  int fd = m_slave_fd;
285317027Sdim  m_slave_fd = invalid_fd;
286317027Sdim  return fd;
287317027Sdim}
288