1 //===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Created by Greg Clayton on 1/8/08.
11 //===----------------------------------------------------------------------===//
13 #include "PseudoTerminal.h"
15 #include <sys/ioctl.h>
18 // PseudoTerminal constructor
19 PseudoTerminal::PseudoTerminal()
20 : m_primary_fd(invalid_fd
), m_secondary_fd(invalid_fd
) {}
23 // The primary and secondary file descriptors will get closed if they are
24 // valid. Call the ReleasePrimaryFD()/ReleaseSecondaryFD() member functions
25 // to release any file descriptors that are needed beyond the lifespan
27 PseudoTerminal::~PseudoTerminal() {
32 // Close the primary file descriptor if it is valid.
33 void PseudoTerminal::ClosePrimary() {
34 if (m_primary_fd
> 0) {
35 ::close(m_primary_fd
);
36 m_primary_fd
= invalid_fd
;
40 // Close the secondary file descriptor if it is valid.
41 void PseudoTerminal::CloseSecondary() {
42 if (m_secondary_fd
> 0) {
43 ::close(m_secondary_fd
);
44 m_secondary_fd
= invalid_fd
;
48 // Open the first available pseudo terminal with OFLAG as the
49 // permissions. The file descriptor is store in the m_primary_fd member
50 // variable and can be accessed via the PrimaryFD() or ReleasePrimaryFD()
53 // Suggested value for oflag is O_RDWR|O_NOCTTY
56 // Zero when successful, non-zero indicating an error occurred.
57 PseudoTerminal::Status
PseudoTerminal::OpenFirstAvailablePrimary(int oflag
) {
58 // Open the primary side of a pseudo terminal
59 m_primary_fd
= ::posix_openpt(oflag
);
60 if (m_primary_fd
< 0) {
61 return err_posix_openpt_failed
;
64 // Grant access to the secondary pseudo terminal
65 if (::grantpt(m_primary_fd
) < 0) {
67 return err_grantpt_failed
;
70 // Clear the lock flag on the secondary pseudo terminal
71 if (::unlockpt(m_primary_fd
) < 0) {
73 return err_unlockpt_failed
;
79 // Open the secondary pseudo terminal for the current primary pseudo
80 // terminal. A primary pseudo terminal should already be valid prior to
81 // calling this function (see PseudoTerminal::OpenFirstAvailablePrimary()).
82 // The file descriptor is stored in the m_secondary_fd member variable and
83 // can be accessed via the SecondaryFD() or ReleaseSecondaryFD() accessors.
86 // Zero when successful, non-zero indicating an error occurred.
87 PseudoTerminal::Status
PseudoTerminal::OpenSecondary(int oflag
) {
90 // Open the primary side of a pseudo terminal
91 const char *secondary_name
= SecondaryName();
93 if (secondary_name
== NULL
)
94 return err_ptsname_failed
;
96 m_secondary_fd
= ::open(secondary_name
, oflag
);
98 if (m_secondary_fd
< 0)
99 return err_open_secondary_failed
;
104 // Get the name of the secondary pseudo terminal. A primary pseudo terminal
105 // should already be valid prior to calling this function (see
106 // PseudoTerminal::OpenFirstAvailablePrimary()).
109 // NULL if no valid primary pseudo terminal or if ptsname() fails.
110 // The name of the secondary pseudo terminal as a NULL terminated C string
111 // that comes from static memory, so a copy of the string should be
112 // made as subsequent calls can change this value.
113 const char *PseudoTerminal::SecondaryName() const {
114 if (m_primary_fd
< 0)
116 return ::ptsname(m_primary_fd
);
119 // Fork a child process that and have its stdio routed to a pseudo
122 // In the parent process when a valid pid is returned, the primary file
123 // descriptor can be used as a read/write access to stdio of the
126 // In the child process the stdin/stdout/stderr will already be routed
127 // to the secondary pseudo terminal and the primary file descriptor will be
128 // closed as it is no longer needed by the child process.
130 // This class will close the file descriptors for the primary/secondary
131 // when the destructor is called, so be sure to call ReleasePrimaryFD()
132 // or ReleaseSecondaryFD() if any file descriptors are going to be used
133 // past the lifespan of this object.
136 // in the parent process: the pid of the child, or -1 if fork fails
137 // in the child process: zero
139 pid_t
PseudoTerminal::Fork(PseudoTerminal::Status
&error
) {
140 pid_t pid
= invalid_pid
;
141 error
= OpenFirstAvailablePrimary(O_RDWR
| O_NOCTTY
);
144 // Successfully opened our primary pseudo terminal
149 error
= err_fork_failed
;
150 } else if (pid
== 0) {
154 error
= OpenSecondary(O_RDWR
);
156 // Successfully opened secondary
157 // We are done with the primary in the child process so lets close it
160 #if defined(TIOCSCTTY)
161 // Acquire the controlling terminal
162 if (::ioctl(m_secondary_fd
, TIOCSCTTY
, (char *)0) < 0)
163 error
= err_failed_to_acquire_controlling_terminal
;
165 // Duplicate all stdio file descriptors to the secondary pseudo terminal
166 if (::dup2(m_secondary_fd
, STDIN_FILENO
) != STDIN_FILENO
)
167 error
= error
? error
: err_dup2_failed_on_stdin
;
168 if (::dup2(m_secondary_fd
, STDOUT_FILENO
) != STDOUT_FILENO
)
169 error
= error
? error
: err_dup2_failed_on_stdout
;
170 if (::dup2(m_secondary_fd
, STDERR_FILENO
) != STDERR_FILENO
)
171 error
= error
? error
: err_dup2_failed_on_stderr
;
175 // Do nothing and let the pid get returned!