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