1061da546Spatrick //===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick // Created by Greg Clayton on 1/8/08.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick
13061da546Spatrick #include "PseudoTerminal.h"
14*be691f3bSpatrick #include <cstdlib>
15061da546Spatrick #include <sys/ioctl.h>
16061da546Spatrick #include <unistd.h>
17061da546Spatrick
18061da546Spatrick // PseudoTerminal constructor
PseudoTerminal()19061da546Spatrick PseudoTerminal::PseudoTerminal()
20dda28197Spatrick : m_primary_fd(invalid_fd), m_secondary_fd(invalid_fd) {}
21061da546Spatrick
22061da546Spatrick // Destructor
23dda28197Spatrick // The primary and secondary file descriptors will get closed if they are
24dda28197Spatrick // valid. Call the ReleasePrimaryFD()/ReleaseSecondaryFD() member functions
25061da546Spatrick // to release any file descriptors that are needed beyond the lifespan
26061da546Spatrick // of this object.
~PseudoTerminal()27061da546Spatrick PseudoTerminal::~PseudoTerminal() {
28dda28197Spatrick ClosePrimary();
29dda28197Spatrick CloseSecondary();
30061da546Spatrick }
31061da546Spatrick
32dda28197Spatrick // Close the primary file descriptor if it is valid.
ClosePrimary()33dda28197Spatrick void PseudoTerminal::ClosePrimary() {
34dda28197Spatrick if (m_primary_fd > 0) {
35dda28197Spatrick ::close(m_primary_fd);
36dda28197Spatrick m_primary_fd = invalid_fd;
37061da546Spatrick }
38061da546Spatrick }
39061da546Spatrick
40dda28197Spatrick // Close the secondary file descriptor if it is valid.
CloseSecondary()41dda28197Spatrick void PseudoTerminal::CloseSecondary() {
42dda28197Spatrick if (m_secondary_fd > 0) {
43dda28197Spatrick ::close(m_secondary_fd);
44dda28197Spatrick m_secondary_fd = invalid_fd;
45061da546Spatrick }
46061da546Spatrick }
47061da546Spatrick
48061da546Spatrick // Open the first available pseudo terminal with OFLAG as the
49dda28197Spatrick // permissions. The file descriptor is store in the m_primary_fd member
50dda28197Spatrick // variable and can be accessed via the PrimaryFD() or ReleasePrimaryFD()
51061da546Spatrick // accessors.
52061da546Spatrick //
53061da546Spatrick // Suggested value for oflag is O_RDWR|O_NOCTTY
54061da546Spatrick //
55061da546Spatrick // RETURNS:
56061da546Spatrick // Zero when successful, non-zero indicating an error occurred.
OpenFirstAvailablePrimary(int oflag)57dda28197Spatrick PseudoTerminal::Status PseudoTerminal::OpenFirstAvailablePrimary(int oflag) {
58dda28197Spatrick // Open the primary side of a pseudo terminal
59dda28197Spatrick m_primary_fd = ::posix_openpt(oflag);
60dda28197Spatrick if (m_primary_fd < 0) {
61061da546Spatrick return err_posix_openpt_failed;
62061da546Spatrick }
63061da546Spatrick
64dda28197Spatrick // Grant access to the secondary pseudo terminal
65dda28197Spatrick if (::grantpt(m_primary_fd) < 0) {
66dda28197Spatrick ClosePrimary();
67061da546Spatrick return err_grantpt_failed;
68061da546Spatrick }
69061da546Spatrick
70dda28197Spatrick // Clear the lock flag on the secondary pseudo terminal
71dda28197Spatrick if (::unlockpt(m_primary_fd) < 0) {
72dda28197Spatrick ClosePrimary();
73061da546Spatrick return err_unlockpt_failed;
74061da546Spatrick }
75061da546Spatrick
76061da546Spatrick return success;
77061da546Spatrick }
78061da546Spatrick
79dda28197Spatrick // Open the secondary pseudo terminal for the current primary pseudo
80dda28197Spatrick // terminal. A primary pseudo terminal should already be valid prior to
81dda28197Spatrick // calling this function (see PseudoTerminal::OpenFirstAvailablePrimary()).
82dda28197Spatrick // The file descriptor is stored in the m_secondary_fd member variable and
83dda28197Spatrick // can be accessed via the SecondaryFD() or ReleaseSecondaryFD() accessors.
84061da546Spatrick //
85061da546Spatrick // RETURNS:
86061da546Spatrick // Zero when successful, non-zero indicating an error occurred.
OpenSecondary(int oflag)87dda28197Spatrick PseudoTerminal::Status PseudoTerminal::OpenSecondary(int oflag) {
88dda28197Spatrick CloseSecondary();
89061da546Spatrick
90dda28197Spatrick // Open the primary side of a pseudo terminal
91dda28197Spatrick const char *secondary_name = SecondaryName();
92061da546Spatrick
93dda28197Spatrick if (secondary_name == NULL)
94061da546Spatrick return err_ptsname_failed;
95061da546Spatrick
96dda28197Spatrick m_secondary_fd = ::open(secondary_name, oflag);
97061da546Spatrick
98dda28197Spatrick if (m_secondary_fd < 0)
99dda28197Spatrick return err_open_secondary_failed;
100061da546Spatrick
101061da546Spatrick return success;
102061da546Spatrick }
103061da546Spatrick
104dda28197Spatrick // Get the name of the secondary pseudo terminal. A primary pseudo terminal
105061da546Spatrick // should already be valid prior to calling this function (see
106dda28197Spatrick // PseudoTerminal::OpenFirstAvailablePrimary()).
107061da546Spatrick //
108061da546Spatrick // RETURNS:
109dda28197Spatrick // NULL if no valid primary pseudo terminal or if ptsname() fails.
110dda28197Spatrick // The name of the secondary pseudo terminal as a NULL terminated C string
111061da546Spatrick // that comes from static memory, so a copy of the string should be
112061da546Spatrick // made as subsequent calls can change this value.
SecondaryName() const113dda28197Spatrick const char *PseudoTerminal::SecondaryName() const {
114dda28197Spatrick if (m_primary_fd < 0)
115061da546Spatrick return NULL;
116dda28197Spatrick return ::ptsname(m_primary_fd);
117061da546Spatrick }
118061da546Spatrick
119061da546Spatrick // Fork a child process that and have its stdio routed to a pseudo
120061da546Spatrick // terminal.
121061da546Spatrick //
122dda28197Spatrick // In the parent process when a valid pid is returned, the primary file
123061da546Spatrick // descriptor can be used as a read/write access to stdio of the
124061da546Spatrick // child process.
125061da546Spatrick //
126061da546Spatrick // In the child process the stdin/stdout/stderr will already be routed
127dda28197Spatrick // to the secondary pseudo terminal and the primary file descriptor will be
128061da546Spatrick // closed as it is no longer needed by the child process.
129061da546Spatrick //
130dda28197Spatrick // This class will close the file descriptors for the primary/secondary
131dda28197Spatrick // when the destructor is called, so be sure to call ReleasePrimaryFD()
132dda28197Spatrick // or ReleaseSecondaryFD() if any file descriptors are going to be used
133061da546Spatrick // past the lifespan of this object.
134061da546Spatrick //
135061da546Spatrick // RETURNS:
136061da546Spatrick // in the parent process: the pid of the child, or -1 if fork fails
137061da546Spatrick // in the child process: zero
138061da546Spatrick
Fork(PseudoTerminal::Status & error)139061da546Spatrick pid_t PseudoTerminal::Fork(PseudoTerminal::Status &error) {
140061da546Spatrick pid_t pid = invalid_pid;
141dda28197Spatrick error = OpenFirstAvailablePrimary(O_RDWR | O_NOCTTY);
142061da546Spatrick
143061da546Spatrick if (error == 0) {
144dda28197Spatrick // Successfully opened our primary pseudo terminal
145061da546Spatrick
146061da546Spatrick pid = ::fork();
147061da546Spatrick if (pid < 0) {
148061da546Spatrick // Fork failed
149061da546Spatrick error = err_fork_failed;
150061da546Spatrick } else if (pid == 0) {
151061da546Spatrick // Child Process
152061da546Spatrick ::setsid();
153061da546Spatrick
154dda28197Spatrick error = OpenSecondary(O_RDWR);
155061da546Spatrick if (error == 0) {
156dda28197Spatrick // Successfully opened secondary
157dda28197Spatrick // We are done with the primary in the child process so lets close it
158dda28197Spatrick ClosePrimary();
159061da546Spatrick
160061da546Spatrick #if defined(TIOCSCTTY)
161061da546Spatrick // Acquire the controlling terminal
162dda28197Spatrick if (::ioctl(m_secondary_fd, TIOCSCTTY, (char *)0) < 0)
163061da546Spatrick error = err_failed_to_acquire_controlling_terminal;
164061da546Spatrick #endif
165dda28197Spatrick // Duplicate all stdio file descriptors to the secondary pseudo terminal
166dda28197Spatrick if (::dup2(m_secondary_fd, STDIN_FILENO) != STDIN_FILENO)
167061da546Spatrick error = error ? error : err_dup2_failed_on_stdin;
168dda28197Spatrick if (::dup2(m_secondary_fd, STDOUT_FILENO) != STDOUT_FILENO)
169061da546Spatrick error = error ? error : err_dup2_failed_on_stdout;
170dda28197Spatrick if (::dup2(m_secondary_fd, STDERR_FILENO) != STDERR_FILENO)
171061da546Spatrick error = error ? error : err_dup2_failed_on_stderr;
172061da546Spatrick }
173061da546Spatrick } else {
174061da546Spatrick // Parent Process
175061da546Spatrick // Do nothing and let the pid get returned!
176061da546Spatrick }
177061da546Spatrick }
178061da546Spatrick return pid;
179061da546Spatrick }
180