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