blob: cac8bda6248fd93385ca8bf61f744b7806c1d580 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- PseudoTerminal.cpp --------------------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Chris Lattner30fdc8d2010-06-08 16:52:24 +00006//
7//===----------------------------------------------------------------------===//
8//
9// Created by Greg Clayton on 1/8/08.
10//
11//===----------------------------------------------------------------------===//
12
13#include "PseudoTerminal.h"
14#include <stdlib.h>
15#include <sys/ioctl.h>
Benjamin Kramerd34a3292011-10-23 16:31:38 +000016#include <unistd.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000017
Chris Lattner30fdc8d2010-06-08 16:52:24 +000018// PseudoTerminal constructor
Kate Stoneb9c1b512016-09-06 20:57:50 +000019PseudoTerminal::PseudoTerminal()
20 : m_master_fd(invalid_fd), m_slave_fd(invalid_fd) {}
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021
Chris Lattner30fdc8d2010-06-08 16:52:24 +000022// Destructor
23// The master and slave file descriptors will get closed if they are
24// valid. Call the ReleaseMasterFD()/ReleaseSlaveFD() member functions
25// to release any file descriptors that are needed beyond the lifespan
26// of this object.
Kate Stoneb9c1b512016-09-06 20:57:50 +000027PseudoTerminal::~PseudoTerminal() {
28 CloseMaster();
29 CloseSlave();
Chris Lattner30fdc8d2010-06-08 16:52:24 +000030}
31
Chris Lattner30fdc8d2010-06-08 16:52:24 +000032// Close the master file descriptor if it is valid.
Kate Stoneb9c1b512016-09-06 20:57:50 +000033void PseudoTerminal::CloseMaster() {
34 if (m_master_fd > 0) {
35 ::close(m_master_fd);
36 m_master_fd = invalid_fd;
37 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +000038}
39
Chris Lattner30fdc8d2010-06-08 16:52:24 +000040// Close the slave file descriptor if it is valid.
Kate Stoneb9c1b512016-09-06 20:57:50 +000041void PseudoTerminal::CloseSlave() {
42 if (m_slave_fd > 0) {
43 ::close(m_slave_fd);
44 m_slave_fd = invalid_fd;
45 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +000046}
47
Chris Lattner30fdc8d2010-06-08 16:52:24 +000048// Open the first available pseudo terminal with OFLAG as the
49// permissions. The file descriptor is store in the m_master_fd member
50// variable and can be accessed via the MasterFD() or ReleaseMasterFD()
51// accessors.
52//
53// Suggested value for oflag is O_RDWR|O_NOCTTY
54//
55// RETURNS:
56// Zero when successful, non-zero indicating an error occurred.
Zachary Turner97206d52017-05-12 04:51:55 +000057PseudoTerminal::Status PseudoTerminal::OpenFirstAvailableMaster(int oflag) {
Kate Stoneb9c1b512016-09-06 20:57:50 +000058 // Open the master side of a pseudo terminal
59 m_master_fd = ::posix_openpt(oflag);
60 if (m_master_fd < 0) {
61 return err_posix_openpt_failed;
62 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +000063
Kate Stoneb9c1b512016-09-06 20:57:50 +000064 // Grant access to the slave pseudo terminal
65 if (::grantpt(m_master_fd) < 0) {
66 CloseMaster();
67 return err_grantpt_failed;
68 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +000069
Kate Stoneb9c1b512016-09-06 20:57:50 +000070 // Clear the lock flag on the slave pseudo terminal
71 if (::unlockpt(m_master_fd) < 0) {
72 CloseMaster();
73 return err_unlockpt_failed;
74 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +000075
Kate Stoneb9c1b512016-09-06 20:57:50 +000076 return success;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000077}
78
Chris Lattner30fdc8d2010-06-08 16:52:24 +000079// Open the slave pseudo terminal for the current master pseudo
80// terminal. A master pseudo terminal should already be valid prior to
81// calling this function (see PseudoTerminal::OpenFirstAvailableMaster()).
82// The file descriptor is stored in the m_slave_fd member variable and
83// can be accessed via the SlaveFD() or ReleaseSlaveFD() accessors.
84//
85// RETURNS:
86// Zero when successful, non-zero indicating an error occurred.
Zachary Turner97206d52017-05-12 04:51:55 +000087PseudoTerminal::Status PseudoTerminal::OpenSlave(int oflag) {
Kate Stoneb9c1b512016-09-06 20:57:50 +000088 CloseSlave();
Chris Lattner30fdc8d2010-06-08 16:52:24 +000089
Kate Stoneb9c1b512016-09-06 20:57:50 +000090 // Open the master side of a pseudo terminal
91 const char *slave_name = SlaveName();
Chris Lattner30fdc8d2010-06-08 16:52:24 +000092
Kate Stoneb9c1b512016-09-06 20:57:50 +000093 if (slave_name == NULL)
94 return err_ptsname_failed;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000095
Kate Stoneb9c1b512016-09-06 20:57:50 +000096 m_slave_fd = ::open(slave_name, oflag);
Chris Lattner30fdc8d2010-06-08 16:52:24 +000097
Kate Stoneb9c1b512016-09-06 20:57:50 +000098 if (m_slave_fd < 0)
99 return err_open_slave_failed;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000100
Kate Stoneb9c1b512016-09-06 20:57:50 +0000101 return success;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000102}
103
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000104// Get the name of the slave pseudo terminal. A master pseudo terminal
105// should already be valid prior to calling this function (see
106// PseudoTerminal::OpenFirstAvailableMaster()).
107//
108// RETURNS:
109// NULL if no valid master pseudo terminal or if ptsname() fails.
110// The name of the slave 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.
Kate Stoneb9c1b512016-09-06 20:57:50 +0000113const char *PseudoTerminal::SlaveName() const {
114 if (m_master_fd < 0)
115 return NULL;
116 return ::ptsname(m_master_fd);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000117}
118
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000119// Fork a child process that and have its stdio routed to a pseudo
120// terminal.
121//
122// In the parent process when a valid pid is returned, the master file
123// descriptor can be used as a read/write access to stdio of the
124// child process.
125//
126// In the child process the stdin/stdout/stderr will already be routed
127// to the slave pseudo terminal and the master file descriptor will be
128// closed as it is no longer needed by the child process.
129//
130// This class will close the file descriptors for the master/slave
131// when the destructor is called, so be sure to call ReleaseMasterFD()
132// or ReleaseSlaveFD() if any file descriptors are going to be used
133// past the lifespan of this object.
134//
135// RETURNS:
136// in the parent process: the pid of the child, or -1 if fork fails
137// in the child process: zero
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000138
Zachary Turner97206d52017-05-12 04:51:55 +0000139pid_t PseudoTerminal::Fork(PseudoTerminal::Status &error) {
Kate Stoneb9c1b512016-09-06 20:57:50 +0000140 pid_t pid = invalid_pid;
141 error = OpenFirstAvailableMaster(O_RDWR | O_NOCTTY);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000142
Kate Stoneb9c1b512016-09-06 20:57:50 +0000143 if (error == 0) {
144 // Successfully opened our master pseudo terminal
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000145
Kate Stoneb9c1b512016-09-06 20:57:50 +0000146 pid = ::fork();
147 if (pid < 0) {
148 // Fork failed
149 error = err_fork_failed;
150 } else if (pid == 0) {
151 // Child Process
152 ::setsid();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000153
Kate Stoneb9c1b512016-09-06 20:57:50 +0000154 error = OpenSlave(O_RDWR);
155 if (error == 0) {
156 // Successfully opened slave
157 // We are done with the master in the child process so lets close it
158 CloseMaster();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000159
Kate Stoneb9c1b512016-09-06 20:57:50 +0000160#if defined(TIOCSCTTY)
161 // Acquire the controlling terminal
162 if (::ioctl(m_slave_fd, TIOCSCTTY, (char *)0) < 0)
163 error = err_failed_to_acquire_controlling_terminal;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000164#endif
Kate Stoneb9c1b512016-09-06 20:57:50 +0000165 // Duplicate all stdio file descriptors to the slave pseudo terminal
166 if (::dup2(m_slave_fd, STDIN_FILENO) != STDIN_FILENO)
167 error = error ? error : err_dup2_failed_on_stdin;
168 if (::dup2(m_slave_fd, STDOUT_FILENO) != STDOUT_FILENO)
169 error = error ? error : err_dup2_failed_on_stdout;
170 if (::dup2(m_slave_fd, STDERR_FILENO) != STDERR_FILENO)
171 error = error ? error : err_dup2_failed_on_stderr;
172 }
173 } else {
174 // Parent Process
175 // Do nothing and let the pid get returned!
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000176 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000177 }
178 return pid;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000179}