Home | History | Annotate | Download | only in source
      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),
     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 //----------------------------------------------------------------------
     35 PseudoTerminal::~PseudoTerminal()
     36 {
     37     CloseMaster();
     38     CloseSlave();
     39 }
     40 
     41 //----------------------------------------------------------------------
     42 // Close the master file descriptor if it is valid.
     43 //----------------------------------------------------------------------
     44 void
     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
     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
     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
    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*
    148 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
    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