• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  Created by Greg Clayton on 1/8/08.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "PseudoTerminal.h"
15 #include <stdlib.h>
16 #include <sys/ioctl.h>
17 #include <unistd.h>
18 
19 //----------------------------------------------------------------------
20 // PseudoTerminal constructor
21 //----------------------------------------------------------------------
PseudoTerminal()22 PseudoTerminal::PseudoTerminal() :
23     m_master_fd(invalid_fd),
24     m_slave_fd(invalid_fd)
25 {
26 }
27 
28 //----------------------------------------------------------------------
29 // Destructor
30 // The master and slave file descriptors will get closed if they are
31 // valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions
32 // to release any file descriptors that are needed beyond the lifespan
33 // of this object.
34 //----------------------------------------------------------------------
~PseudoTerminal()35 PseudoTerminal::~PseudoTerminal()
36 {
37     CloseMaster();
38     CloseSlave();
39 }
40 
41 //----------------------------------------------------------------------
42 // Close the master file descriptor if it is valid.
43 //----------------------------------------------------------------------
44 void
CloseMaster()45 PseudoTerminal::CloseMaster()
46 {
47     if (m_master_fd > 0)
48     {
49         ::close (m_master_fd);
50         m_master_fd = invalid_fd;
51     }
52 }
53 
54 //----------------------------------------------------------------------
55 // Close the slave file descriptor if it is valid.
56 //----------------------------------------------------------------------
57 void
CloseSlave()58 PseudoTerminal::CloseSlave()
59 {
60     if (m_slave_fd > 0)
61     {
62         ::close (m_slave_fd);
63         m_slave_fd = invalid_fd;
64     }
65 }
66 
67 //----------------------------------------------------------------------
68 // Open the first available pseudo terminal with OFLAG as the
69 // permissions. The file descriptor is store in the m_master_fd member
70 // variable and can be accessed via the MasterFD() or ReleaseMasterFD()
71 // accessors.
72 //
73 // Suggested value for oflag is O_RDWR|O_NOCTTY
74 //
75 // RETURNS:
76 //  Zero when successful, non-zero indicating an error occurred.
77 //----------------------------------------------------------------------
78 PseudoTerminal::Error
OpenFirstAvailableMaster(int oflag)79 PseudoTerminal::OpenFirstAvailableMaster(int oflag)
80 {
81     // Open the master side of a pseudo terminal
82     m_master_fd = ::posix_openpt (oflag);
83     if (m_master_fd < 0)
84     {
85         return err_posix_openpt_failed;
86     }
87 
88     // Grant access to the slave pseudo terminal
89     if (::grantpt (m_master_fd) < 0)
90     {
91         CloseMaster();
92         return err_grantpt_failed;
93     }
94 
95     // Clear the lock flag on the slave pseudo terminal
96     if (::unlockpt (m_master_fd) < 0)
97     {
98         CloseMaster();
99         return err_unlockpt_failed;
100     }
101 
102     return success;
103 }
104 
105 //----------------------------------------------------------------------
106 // Open the slave pseudo terminal for the current master pseudo
107 // terminal. A master pseudo terminal should already be valid prior to
108 // calling this function (see PseudoTerminal::OpenFirstAvailableMaster()).
109 // The file descriptor is stored in the m_slave_fd member variable and
110 // can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors.
111 //
112 // RETURNS:
113 //  Zero when successful, non-zero indicating an error occurred.
114 //----------------------------------------------------------------------
115 PseudoTerminal::Error
OpenSlave(int oflag)116 PseudoTerminal::OpenSlave(int oflag)
117 {
118     CloseSlave();
119 
120     // Open the master side of a pseudo terminal
121     const char *slave_name = SlaveName();
122 
123     if (slave_name == NULL)
124         return err_ptsname_failed;
125 
126     m_slave_fd = ::open (slave_name, oflag);
127 
128     if (m_slave_fd < 0)
129         return err_open_slave_failed;
130 
131     return success;
132 }
133 
134 
135 
136 //----------------------------------------------------------------------
137 // Get the name of the slave pseudo terminal. A master pseudo terminal
138 // should already be valid prior to calling this function (see
139 // PseudoTerminal::OpenFirstAvailableMaster()).
140 //
141 // RETURNS:
142 //  NULL if no valid master pseudo terminal or if ptsname() fails.
143 //  The name of the slave pseudo terminal as a NULL terminated C string
144 //  that comes from static memory, so a copy of the string should be
145 //  made as subsequent calls can change this value.
146 //----------------------------------------------------------------------
147 const char*
SlaveName() const148 PseudoTerminal::SlaveName() const
149 {
150     if (m_master_fd < 0)
151         return NULL;
152     return ::ptsname (m_master_fd);
153 }
154 
155 
156 //----------------------------------------------------------------------
157 // Fork a child process that and have its stdio routed to a pseudo
158 // terminal.
159 //
160 // In the parent process when a valid pid is returned, the master file
161 // descriptor can be used as a read/write access to stdio of the
162 // child process.
163 //
164 // In the child process the stdin/stdout/stderr will already be routed
165 // to the slave pseudo terminal and the master file descriptor will be
166 // closed as it is no longer needed by the child process.
167 //
168 // This class will close the file descriptors for the master/slave
169 // when the destructor is called, so be sure to call ReleaseMasterFD()
170 // or ReleaseSlaveFD() if any file descriptors are going to be used
171 // past the lifespan of this object.
172 //
173 // RETURNS:
174 //  in the parent process: the pid of the child, or -1 if fork fails
175 //  in the child process: zero
176 //----------------------------------------------------------------------
177 
178 pid_t
Fork(PseudoTerminal::Error & error)179 PseudoTerminal::Fork(PseudoTerminal::Error& error)
180 {
181     pid_t pid = invalid_pid;
182     error = OpenFirstAvailableMaster (O_RDWR|O_NOCTTY);
183 
184     if (error == 0)
185     {
186         // Successfully opened our master pseudo terminal
187 
188         pid = ::fork ();
189         if (pid < 0)
190         {
191             // Fork failed
192             error = err_fork_failed;
193         }
194         else if (pid == 0)
195         {
196             // Child Process
197             ::setsid();
198 
199             error = OpenSlave (O_RDWR);
200             if (error == 0)
201             {
202                 // Successfully opened slave
203                 // We are done with the master in the child process so lets close it
204                 CloseMaster ();
205 
206 #if defined (TIOCSCTTY)
207                 // Acquire the controlling terminal
208                 if (::ioctl (m_slave_fd, TIOCSCTTY, (char *)0) < 0)
209                     error = err_failed_to_acquire_controlling_terminal;
210 #endif
211                 // Duplicate all stdio file descriptors to the slave pseudo terminal
212                 if (::dup2 (m_slave_fd, STDIN_FILENO) != STDIN_FILENO)
213                     error = error ? error : err_dup2_failed_on_stdin;
214                 if (::dup2 (m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO)
215                     error = error ? error : err_dup2_failed_on_stdout;
216                 if (::dup2 (m_slave_fd, STDERR_FILENO) != STDERR_FILENO)
217                     error = error ? error : err_dup2_failed_on_stderr;
218             }
219         }
220         else
221         {
222             // Parent Process
223             // Do nothing and let the pid get returned!
224         }
225     }
226     return pid;
227 }
228