blob: 278ab88828a7665cfb4f357f212bff5fd8263517 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- 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//----------------------------------------------------------------------
21PseudoTerminal::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//----------------------------------------------------------------------
34PseudoTerminal::~PseudoTerminal()
35{
36 CloseMaster();
37 CloseSlave();
38}
39
40//----------------------------------------------------------------------
41// Close the master file descriptor if it is valid.
42//----------------------------------------------------------------------
43void
44PseudoTerminal::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//----------------------------------------------------------------------
56void
57PseudoTerminal::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//----------------------------------------------------------------------
77PseudoTerminal::Error
78PseudoTerminal::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//----------------------------------------------------------------------
114PseudoTerminal::Error
115PseudoTerminal::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//----------------------------------------------------------------------
146const char*
147PseudoTerminal::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
177pid_t
178PseudoTerminal::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}